先下载
http://www.eclipse.org/mat/downloads.php
配置环境参数
分析一个堆转储文件需要消耗很多的堆空间,为了保证分析的效率和性能,在有条件的情况下,建议分配给 MAT 尽可能多的内存资源。
编辑文件 MemoryAnalyzer.ini,在里面添加类似信息 -vmargs – Xmx4g。
原文件
-startup plugins/org.eclipse.equinox.launcher_1.5.0.v20180512-1130.jar --launcher.library plugins/org.eclipse.equinox.launcher.win32.win32.x86_64_1.1.700.v20180518-1200 -vmargs -Xmx1024m |
修改后
-startup plugins/org.eclipse.equinox.launcher_1.5.0.v20180512-1130.jar --launcher.library plugins/org.eclipse.equinox.launcher.win32.win32.x86_64_1.1.700.v20180518-1200 -vmargs -Xmx12g |
获得堆转储文件
有很多的工具,例如 JMap,JConsole 都可以帮助我们得到一个堆转储文件,如下命令:
JMap -dump:format=b,file=<dumpfile> <pid>
生成分析报告
首先,启动前面安装配置好的 Memory Analyzer tool , 然后选择菜单项 File- Open Heap Dump 来加载需要分析的堆转储文件。
文件加载完成后,你可以看到界面:
通过上面的概览,我们对内存占用情况有了一个总体的了解。
先检查一下 MAT 生成的一系列文件。
图 2. 文件列表
可以看到 MAT 工具提将报告的内容压缩打包到一个 zip 文件,并把它存放到原始堆转储文件的存放目录下,
这样如果您需要和同事一起分析这个内存问题的话,只需要把这个小小的 zip 包发给他就可以了,不需要把整个堆文件发给他。
并且整个报告是一个 HTML 格式的文件,用浏览器就可以轻松打开。
接下来我们就可以来看看生成的报告都包括什么内容,能不能帮我们找到问题所在。
点击工具栏上的 Leak Suspects 菜单项来生成内存泄露分析报告,也可以直接点击饼图下方的 Reports->Leak Suspects 链接来生成报告。
图 3. 工具栏菜单
分析三步曲
通常会采用下面的“三步曲”来分析内存泄露问题:
第一步,对问题发生时刻的系统内存状态获取一个整体印象。
第二步,找到最有可能导致内存泄露的元凶,通常也就是消耗内存最多的对象
第三步,进一步去查看这个内存消耗大户的具体情况,看看是否有什么异常的行为。
查看报告之一:内存消耗的整体状况
内存分析报告
如上图所示,在报告上最醒目的就是一张简洁明了的饼图,从图上我们可以清晰地看到一个可疑对象消耗了系统 *% 的内存。
在图的下方还有对这个可疑对象的进一步描述。我们可以看到内存是由org.elasticsearch.common.util.PageCacheRecycler的实例消耗的,sun.misc.Launcher$AppClassLoader 负责这个对象的加载。
这段描述非常短,但已经可以从中找到很多线索了,比如是哪个类占用了绝大多数的内存,它属于哪个组件等等。
接下来,我们应该进一步去分析问题,为什么一个 PageCacheRecycler会占据了系统 *% 的内存,谁阻止了垃圾回收机制对它的回收。
查看报告之二:分析问题的所在
首先我们简单回顾下 JAVA 的内存回收机制,内存空间中垃圾回收的工作由垃圾回收器 (Garbage Collector,GC) 完成的,它的核心思想是:对虚拟机可用内存空间,即堆空间中的对象进行识别,如果对象正在被引用,那么称其为存活对象,反之,如果对象不再被引用,则为垃圾对象,可以回收其占据的空间,用于再分配。
在垃圾回收机制中有一组元素被称为根元素集合,它们是一组被虚拟机直接引用的对象,比如,正在运行的线程对象,系统调用栈里面的对象以及被 system class loader 所加载的那些对象。堆空间中的每个对象都是由一个根元素为起点被层层调用的。因此,一个对象还被某一个存活的根元素所引用,就会被认为是存活对象,不能被回收,进行内存释放。因此,我们可以通过分析一个对象到根元素的引用路径来分析为什么该对象不能被顺利回收。如果说一个对象已经不被任何程序逻辑所需要但是还存在被根元素引用的情况,我们可以说这里存在内存泄露。
现在,让我们开始真正的寻找内存泄露之旅,点击“Details ”链接,可以看到下图所示对可疑对象 1 的详细分析报告。
可疑对象的详细分析报告
- 我们查看下从 GC 根元素到内存消耗聚集点的最短路径:
从根元素到内存消耗聚集点的最短路径
我们可以很清楚的看到整个引用链,内存聚集点是一个拥有大量对象的集合,如果你对代码比较熟悉的话,相信这些信息应该能给你提供一些找到内存泄露的思路了。
查看报告之三:分析问题原因
接下来,我们再继续看看,这个对象集合里到底存放了什么,为什么会消耗掉如此多的内存。
内存消耗聚集对象信息
在这张图上,我们可以清楚的看到,这个对象集合中保存了大量 ***对象的引用,就是它导致的内存泄露。
至此,我们已经拥有了足够的信息去寻找泄露点 :
总结
从上面的例子我们可以看到用 MAT 来进行堆转储文件分析,寻找内存泄露非常简单,尤其是对于新手而言,这是一个很好的辅助分析工具。
但是,MAT 绝对不仅仅是一个“傻瓜式”内存分析工具,它还提供很多高级功能,比如 MAT 支持用 OQL(Object Query Language)对 heap dump 中的对象进行查询,支持对线程的分析等,有关这些功能的使用可以参考 MAT 的帮助文档。