GC root & 使用MAT分析java堆

当咱们的java程序遇到频繁full gc或者oom的时候,咱们经常须要将当前的heap dump出来进行进一步的分析。MAT是用于分析heap dump的神器。html

1 生成heap dump

heap dump是jvm内存中某一时刻全部对象的的快照。一般用于定位java程序的内存泄露或者优化内存。一般能够经过如下几种方式生称dump文件:java

1.1 jmap正则表达式

jmap -dump:[live,]format=b,file= dom

live是可选项,若是加上了live,那么只会dump存活的对象,不会dump将被gc的对象。 jmap的使用举例来讲,假如经过jps获得进程id为19234:eclipse

jmap -dump:format=b,file=heap.hprof 19234jvm

注意: jmap是实验性质的,而且不会长久支持的(This command is experimental and unsupported)jsp

1.2 jcmd
jcmd的功能很是多,用来向jvm发送请求。使用jcmd命令必须是在和jvm进程同一个机器上运行。使用jcmd生成head dump的命令是:字体

jcmd GC.heap_dump [-all] 优化

从试验来看,这里的file-path需要是绝对路径,不能是相对路径。 all是可选项,不写all就相似jmap写上了live。使用举例如:ui

jcmd 19234 GC.heap_dump -all /tmp/dump.hprof

1.3 自动捕获head dump文件
能够经过加入jvm参数,当程序出现oom的时候,自动产生heap dump文件

java -XX:+HeapDumpOnOutOfMemoryError

该参数默认状况下会在咱们启动java进程的目录下,产生一个名字叫 java_pid .hprof 的head dump文件。若是咱们但愿将head dump生成在其余目录或者文件,可使用以下参数:

java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=

固然还有其余生成head dump文件的方式,具体能够参考java-heap-dump-capture

2 GC root

2.1 概念

Garbage Collection Roots.

深刻理解java虚拟机中提到,可做为GC Roots的对象包括下面几种:

  1. 虚拟机栈(栈帧中的本地变量表)中引用的对象
  2. 方法区中类静态属性引用的对象
  3. 方法去中常量引用的对象
  4. 本地方法栈JNI引用的对象

怎么理解呢?

  1. 首先对于上述的第一点应该注意是一个虚拟机栈而不是方法栈,每一个java线程有一个虚拟机栈
  2. 其次为何这些能够做为GC root,其实比较好理解,由于栈中的对象确定是正在使用的,因此能够从这些对象开始遍历,而后得出全部还在引用的对象;
  3. 最后,GC root究竟是对象仍是引用呢?能够理解为这里的引用就是对象,由于对于Java语言(非字节码)来讲单独的引用(没有指向对象的引用)没有意义。也就是说咱们写了个引用可是没有赋值,其实和没写是同样的。能够经过2.2的举例来加深下理解

2.2 举例

咱们能够用MAT来更详细的理解:

public static void main(String[] args) throws Exception {
        Stu stu = new Stu();
        stu.teacher = new Teacher();

        while (true) {
            Thread.sleep(1000);
        }
}

使用jcmd生成heap dump文件,用MAT打开后,搜索Teacher,而后咱们看下这个类对应对象的“Path to gc root”:

gc root

小黄点表示这是个GC root. 这里具体表示这个是当前线程栈中的 变量,类型是上一行的Stu类型。因此这里GC root就是引用了一个Stu对象的栈中的引用,你也能够理解为这个 就是个Stu的一个对象。

注意:一个对象能够有多个GC root,一样在MAT上看也就是多条“Path to gc root”

3 使用MAT分析heap dump

了解了如何生成heap dump和对gc root有了进一步的了解,咱们能够用MAT来进一步分析heap dump

3.1 打开

MAT默认没有显示unreachable objects,在使用前咱们先勾选上

Preferences -> Memory Analyzer

而后勾选上Keep unreachable objects

若是以前没有勾选,后面要改的话,不会马上生效,须要把解析的文件删除掉,从新解析打开heap dump文件

而后打开文件

File -> Open Heap Dump

3.2 Overview

Overview 显示了java堆的一些基本信息,好比大小、对象个数等,也包括一个对象所占内存比例的饼图,有助于咱们直观上去查看占用内存比较大的对象

3.3 Histogram

Histogram即直方图,是以类的粒度来显示,可使用正则表达式搜索感兴趣的类

如图中咱们搜索Teacher,出现一个匹配项;Objects列为1,表示有一个Teacher的对象;Shallow Heap和Retained Heap的概念不在这里阐述了,简单来讲Shallow Heap就是对象自己的大小,Retained Heap是指当对象释放后,引发其余对象释放总共大小,Retained Heap和支配树(dominator tree)概念有关系。通常状况下在分析的时候,咱们会按照Retained Heap大小来排序,占用比较大的颇有可能就是引发oom的对象。

前面说了Histogram是类粒度的,能够右击来显示该类的对象

“with incomming references”表示显示对象和引用该对象的对象,以下图。左边的黑色字体表示变量名,而变量名的类型是它的上一行的左边的类。

看到对象后,咱们通常右击来看下对象的GC root,来肯定对象没有被释放的缘由,有两个选项

  1. 右击 -> path to gc roots -> exclude all phantom/weak/soft etc. references
  2. 右击 -> merge shortest paths to gc roots -> exclude all phantom/weak/soft etc. references

两个的区别是1是显示从该对象到gc roots的路径,并且会显示全部的gc roots(一个对象的gc root能够有多个); 2显示的是从gc roots到对象的路径,并且只显示最短的一条路径。 gc root的显示在2.2中已经显示过了
gc root

通常经过分析gc root的路径和逻辑代码,就能够很容易肯定oom或者内存泄露的缘由了

3.4 dominator tree

dominator tree即支配树。支配树的概念能够参考 支配树。须要注意的是,支配树并不等于path to gc roots。

Histogram是类粒度的,能够找到哪一个类占用的堆内存比较多;dominator tree是对象粒度的,能够用来查看哪一个对象引发占用堆内存比较大。

4 总结

通常来讲对heap dump的分析是个比较综合的过程,经过Histogram和dominator tree,经过gc roots和源码综合分析,能够得出最后的结论

5 参考

  1. https://www.baeldung.com/java-heap-dump-capture
  2. https://help.eclipse.org/luna/index.jsp?topic=%2Forg.eclipse.mat.ui.help%2Fconcepts%2Fgcroots.html&cp=37_2_3
  3. https://stackoverflow.com/questions/26232733/thread-as-a-gc-root
  4. https://www.zhihu.com/question/47258557
  5. https://blog.csdn.net/jji8877032/article/details/84503063
  6. http://www.lightskystreet.com/2015/09/01/mat_usage/
相关文章
相关标签/搜索