背景介绍
MAT(Memory Analyzer Tool),一个基于Eclipse的内存分析工具,是一个快速、功能丰富的JAVA heap分析工具,它可以帮助我们查找内存泄漏和减少内存消耗。使用内存分析工具从众多的对象中进行分析,快速的计算出在内存中对象的占用大小,看看是谁阻止了垃圾收集器的回收工作,并可以通过报表直观的查看到可能造成这种结果的对象。
下载安装
1. 一种安装方式是将MAT当做eclipse的插件进行安装:启动Eclipse --> Help --> Eclipse Marketplace,然后搜索Memory Analyzer,安装,重启eclipse即可
2.另外一种安装方式是将MAT作为一个独立的软件进行安装:去官网http://www.eclipse.org/mat/downloads.php,根据操作系统版本下载最新的MAT。下载后解压就可以运行了。
Dump的方法
在使用mat工具分析内存占用的时候,我们首先需要导出相应的dump文件,通常也是有两种方式
1.Jmap命令直接生成dump文件
进入jdk目录下,使用jmap -dump:format=b,file=test.dump pid将整个堆区dump,保存为二进制文件
2.在JAVA_OPTIONS变量中增加
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${目录}。
当JVM发生OOM时,自动生成DUMP文件。
-XX:HeapDumpPath=${目录}参数表示生成DUMP文件的路径,也可以指定文件名称,例如:-XX:HeapDumpPath=${目录}/java_heapdump.hprof。如果不指定文件名,默认为:java_
图表使用介绍
当成功启动MAT后,通过菜单选项“File->Open heap dump...”打开指定的dump文件后,将会生成Overview选项,我们可以看到总共使用了1G的内存,其中最大的单个对象是8.4M,如下所示:
点击中间那一列Report的第一项leak suspect,工具会自动检测存疑的泄漏点,并报告出那些存活的对象以及这些对象没有被gc的原因。如我们可以看到堆区里占比最高的对象,可以看到哪些线程没有被释放,很可能就是内存泄漏的风险点,所以就可以直接点击Details重点分析了。
MAT工具中,还有很多有用的图表可以帮助我们分析问题,左下方第一个图表HIstogram直方图:可以列出内存中的对象,对象的个数及大小。它按类名将所有的实例对象列出来,可以点击表头进行排序,在表的第一行可以输入正则表达式来匹配结果。比如:.*jmeter.*
还有一个很常用的图表就是Dorminator Tree支配树,可以直观地反映一个对象的retained heap,这里我们首先要了解两个概念,shallow heap和retained heap:
Shallow Size是指对象自身占用的内存大小,不包括它引用的对象。
Retained Size = 当前对象大小+ 当前对象可直接或间接引用到的对象的大小总和。换句话说,Retained Size就是当前对象被GC后,从Heap上总共能释放掉的内存。
不过,释放的时候,还要排查被GC Roots直接或间接引用的对象,他们暂时不会被当前为Garbage。
一般我们重点关注Shallow Size和Retained Size占用的内存空间即可,如果Shallow Size很小,Retained Size很大,说明该对象被引用的很多,可能会有OOM风险。
那么又如何来确定到底是谁在持有这些对象而导致内存没有被回收呢?可以右键选择List Objects -> with incoming reference,这可以列出所有持有这些对象的引用路径:
incoming references:表示该对象的入节点(引用到该对象的对象)
outgoing references:表示该对象的出节点(被该对象引用的对象)
另外查看一个对象到RC Roots的引用链,通常在排查内存泄漏的时候,我们会选择exclude all phantom/weak/soft etc.references, 意思是查看排除虚引用/弱引用/软引用等的引用链,因为被虚引用/弱引用/软引用的对象可以直接被GC给回收,我们要看的就是某个对象否还存在Strong 引用链,如果有,则说明存在内存泄漏,然后再去排查具体引用。
内存快照对比
再介绍一种很有用的方式叫内存快照对比,为查找内存泄漏,通常需要两个 Dump结果作对比,在应用程序启动的时候导出一份dump文件,当程序运行一段时间之后再导出一份文件,通过对比我们可以发现快速增长的对象,常常就是内存泄漏的风险点。
打开 Navigator History面板,将两个表的 Histogram结果都添加到 Compare Basket中去 :
点击右上角的!按钮,将得到比对结果。
小技巧
当目的不明确时,可以直接定位到RetainedHeap最大的Object,Select outcoming references ,查看引用链,定位到可疑的对象然后Path to GC Roots进行引用链分析
如果大对象筛选看不出区别,可以试试按照class分组,再寻找可疑对象进行GC引用链分析。