使用java springboot开发了一个项目来调用cdp4j来操作chrome浏览器,但是调用浏览器访问页面越多,占用内存就越大,直到内存溢出
这就是典型的调用第三方框架,导致的内存溢出,而并不清楚第三方框架中哪个代码导致了 内存溢出
1.异常信息:
java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3332)
at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:596)
at java.lang.StringBuilder.append(StringBuilder.java:190)
at com.google.gson.stream.JsonReader.nextQuotedValue(JsonReader.java:1029)
at com.google.gson.stream.JsonReader.nextString(JsonReader.java:816)
at com.google.gson.internal.bind.TypeAdapters$29.read(TypeAdapters.java:702)
at com.google.gson.internal.bind.TypeAdapters$29.read(TypeAdapters.java:723)
at com.google.gson.internal.bind.TypeAdapters$29.read(TypeAdapters.java:723)
at com.google.gson.internal.bind.TypeAdapters$29.read(TypeAdapters.java:698)
at com.google.gson.internal.bind.TypeAdapters$35$1.read(TypeAdapters.java:894)
at com.google.gson.Gson.fromJson(Gson.java:932)
at com.google.gson.Gson.fromJson(Gson.java:897)
at com.google.gson.Gson.fromJson(Gson.java:846)
at com.google.gson.Gson.fromJson(Gson.java:817)
at io.webfolder.cdp.session.MessageHandler.lambda$process$1(MessageHandler.java:69)
at io.webfolder.cdp.session.MessageHandler$$Lambda$671/1544658000.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
2.原因分析:
2.1内存持续变大的原因只有一个,那就是对象一直被持有,垃圾回收不了,查看一下哪些变量和对象占用内存最多
jmap命令是一个可以输出所有内存中对象的工具
$ jmap -histo:live 32556|head -n 20
num #instances #bytes class name
----------------------------------------------
1: 79360 5422368 [C
2: 79194 1900656 java.lang.String
3: 32489 1559472 com.google.gson.internal.LinkedTreeMap$Node
4: 6271 1167296 [I
5: 10577 1164536 java.lang.Class
6: 35775 1144800 java.util.concurrent.ConcurrentHashMap$Node
7: 9031 1143848 [Ljava.lang.Object;
8: 4302 919928 [B
9: 36983 591728 java.lang.Object
10: 4500 504000 java.net.SocksSocketImpl
11: 10177 488496 com.google.gson.internal.LinkedTreeMap
12: 5369 472472 java.lang.reflect.Method
13: 10314 412560 java.lang.ref.Finalizer
14: 8625 345000 java.util.LinkedHashMap$Entry
15: 173 339328 [Ljava.util.concurrent.ConcurrentHashMap$Node;
16: 3099 289152 [Ljava.util.HashMap$Node;
17: 16221 259536 com.google.gson.JsonPrimitive
2.2将内存信息导出到文件中
$ jmap -dump:format=b,file=filename.bin 32556
Dumping heap to /home/server/filename.bin ...
Heap dump file created
2.3使用Memory Analyzer (MAT)工具分析内存信息
mat主页 https://www.eclipse.org/mat/
下载地址:https://www.eclipse.org/mat/downloads.php
下载后先修改MemoryAnalyzer.ini 修改里面的参数如-Xmx5120m(最好要大于你的堆转储文件)
然后打开 MemoryAnalyzer.exe
然后打开导出的内存文件:文件-OPEN-Heap Dump
“Leak Suspects"一般会显示MAT它自己认为有问题的内容
同时在你导入内存文件的同级文件会产生一个html版的报告,filename_Leak_Suspects.zip。这就相当于一份离线的报告,和界面上的一样只是方便保存,毕竟分析也是需要时间的,但并没有界面上功能齐全。
点击推荐问题的Detail,进入到详情页
直接查看 Accumulated Objects in Dominator Tree 来查看内存是在哪个对象中被占着,并且点击一下,然后选择List object-with outgoing references,因为我的很明显有个static 数组,我是用来作为连接池的,这个连接池里面持有的对象,不断变大,我需要用with outgoing references来查看具体持有的哪个对象在不断增大
接着你只需要点击占用内存大的对象一步一步查找,主要是看 shallow heap(浅堆) 和 retained heap(深堆)
然后发现一个hashmap累计了大量的对象占用了内存
现在你只需要清空这个map或者将持有这个map的对象设置为null,内存就会自动回收!