linux MAT排查解决java项目内存溢出 OutOfMemoryError: Java heap space

java | 2020-02-25 08:39:14

使用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,内存就会自动回收!

 

 

 

登录后即可回复 登录 | 注册
    
关注编程学问公众号