头图

简单来演示一下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文件,如果想要分析内存泄露的话,就勾选红色框框。

这里,我们首先看看那些对象占用大量内存。按红色框框Histogram按钮

第一,哪些对象占用大量内存

进入histogram页面

进来页面后,你一眼就能发现哪个对象占用的内存最多。

比如这里就是明显的class com.hailintang.demo.jdk8.gc.oom.Dandan这个类占用了过多内存。

这里说了,这个Dandan类的对象有360146个。到目前为止,我们暂时确定了Dandan对象占用了大量内存。

第二,对象被谁引用

接下来,我们看看内存过多的对象是被谁引用。

那这个时候需要用到MAT的dominator-tree:就是用来分析对象之间的关系的工具。

然后你可以看到哪些线程创建了过多的对象。

比如这里创建过多对象的线程就是main thread

然后你展开这个main thread看看到底创建了什么对象

使得main thread占用这么多内存

点开一看,好家伙

发现是一个java.lang.Object[]数组。

继续展开

发现数组里面全是Dandan对象

说到这里,其实就真相大白了

经过层层展开。main thread线程创建的对象和在histogram界面中占用过多内存的对象Dandan对上了。

第三,定位到具体哪行代码,进行分析问题

找到引用后,最后一步就是要定位一下具体是哪行代码创建了这么多对象?

这个时候需要另一个视图thread_overview。红圈所示

thread_overview的功能:展示出JVM的所有线程以及每个线程以及线程当时的方法调用栈,还有每个方法创建了哪些对象。

特别说明一下:栈,后进先出,所以,看图的时候,从下往上看

进入thread_overview界面

找到main thread线程,点开看看

你可以快速看到Demo4.java:11

这个时候,你还不是特别确定的。需要点开继续看看

可以看到这里的确创建了大量的Dandan对象。

到这里你再结合一下大头菜的代码:

好了,到这里,基本上已经找到了具体是哪行代码引起的OOM问题。

简单总结一下

总结

我们的方法论是:

  • 第一,哪些对象占用大量内存。------对应的MAT的histogram
  • 第二,对象被谁引用。(就是要知道为什么它无法释放的意思)------对应MAT的dominator-tree
  • 第三,定位到具体哪行代码,进行分析问题----对应MAT的Thread-overview

然后,我们根据方法论,结合MAT工具,一个一个步骤来进行排查。目的是为了找到具体哪行代码引起的OOM问题。

上面这个是一个比较简单的OOM问题。只是一个Demo,目的是为了告诉你如何使用MAT来分析OOM问题。

如果复杂一点的项目,在第三步中,如果出问题的代码是其他第三方中间件,比如tomcat,jetty,rpc等框架引起的OOM,这个时候,还需要你对这些中间件比较熟悉才能定位问题。如果出问题的代码是业务代码,这个时候,是需要邀请负责该项目的开发工程师来进行定位代码的。

其实,大家完全可以把MAT这个定位OOM问题的方法论,用到实际工作。

好了,今天的技术分享就到这里。

有问题,欢迎留言。


大头菜
41 声望8 粉丝