简单来演示一下OOM的分析和实战。
直接上代码:
public class Demo4 {
public static void main(String[] args) {
List<Dandan> list = new ArrayList<>();
while (true){
list.add(new Dandan());
}
}
}
class Dandan{
}
JVM参数:
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC -Xms10m -Xmx10m -XX:+PrintGCDetails -Xloggc:gc_dandan.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./
运行后的日志:
java.lang.OutOfMemoryError: Java heap space
Dumping heap to ./java_pid22788.hprof ...
Heap dump file created [13244840 bytes in 0.050 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.ArrayList.grow(ArrayList.java:267)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:241)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:233)
at java.util.ArrayList.add(ArrayList.java:464)
at com.hailintang.demo.jdk8.gc.oom.Demo4.main(Demo4.java:11)
Process finished with exit code 1
java.lang.OutOfMemoryError: Java heap space
首先,这里明确告诉我们内存泄露的地方是:堆
因为配置了参数:-XX:+HeapDumpOnOutOfMemoryError
会在程序发生OOM的时候,自动生成一个文件:java_pid22788.hprof
命名格式为:java_pid(程序的进程号).hprof
接下来当然是用MAT来分析:hprof文件
在分析之前,我们重点关注什么?
- 第一,哪些对象占用大量内存。
- 第二,对象被谁引用。(就是要知道为什么它无法释放的意思)
- 第三,定位到具体哪行代码,进行分析问题
打开hprof文件,如果想要分析内存泄露的话,就勾选红色框框。
image这里,我们首先看看那些对象占用大量内存。按红色框框Histogram按钮
image第一,哪些对象占用大量内存
进入histogram页面
image进来页面后,你一眼就能发现哪个对象占用的内存最多。
比如这里就是明显的class com.hailintang.demo.jdk8.gc.oom.Dandan这个类占用了过多内存。
这里说了,这个Dandan类的对象有360146个。到目前为止,我们暂时确定了Dandan对象占用了大量内存。
第二,对象被谁引用
接下来,我们看看内存过多的对象是被谁引用。
那这个时候需要用到MAT的dominator-tree:就是用来分析对象之间的关系的工具。
image然后你可以看到哪些线程创建了过多的对象。
比如这里创建过多对象的线程就是main thread
image然后你展开这个main thread看看到底创建了什么对象
使得main thread占用这么多内存
image点开一看,好家伙
发现是一个java.lang.Object[]数组。
继续展开
发现数组里面全是Dandan对象
说到这里,其实就真相大白了
经过层层展开。main thread线程创建的对象和在histogram界面中占用过多内存的对象Dandan对上了。
第三,定位到具体哪行代码,进行分析问题
找到引用后,最后一步就是要定位一下具体是哪行代码创建了这么多对象?
这个时候需要另一个视图thread_overview。红圈所示
thread_overview的功能:展示出JVM的所有线程以及每个线程以及线程当时的方法调用栈,还有每个方法创建了哪些对象。
特别说明一下:栈,后进先出,所以,看图的时候,从下往上看
image进入thread_overview界面
image找到main thread线程,点开看看
image你可以快速看到Demo4.java:11
这个时候,你还不是特别确定的。需要点开继续看看
image
可以看到这里的确创建了大量的Dandan对象。
到这里你再结合一下大头菜的代码:
image好了,到这里,基本上已经找到了具体是哪行代码引起的OOM问题。
简单总结一下
总结
我们的方法论是:
- 第一,哪些对象占用大量内存。------对应的MAT的histogram
- 第二,对象被谁引用。(就是要知道为什么它无法释放的意思)------对应MAT的dominator-tree
- 第三,定位到具体哪行代码,进行分析问题----对应MAT的Thread-overview
然后,我们根据方法论,结合MAT工具,一个一个步骤来进行排查。目的是为了找到具体哪行代码引起的OOM问题。
上面这个是一个比较简单的OOM问题。只是一个Demo,目的是为了告诉你如何使用MAT来分析OOM问题。
如果复杂一点的项目,在第三步中,如果出问题的代码是其他第三方中间件,比如tomcat,jetty,rpc等框架引起的OOM,这个时候,还需要你对这些中间件比较熟悉才能定位问题。如果出问题的代码是业务代码,这个时候,是需要邀请负责该项目的开发工程师来进行定位代码的。
其实,大家完全可以把MAT这个定位OOM问题的方法论,用到实际工作。
好了,今天的技术分享就到这里。
有问题,欢迎留言。
网友评论