天天看点

Jmap+MAT 排查内存泄漏

最近在项目中自测的时候(压力测试)遇到内存泄漏的情况,想查看具体是哪个模块甚至哪个类引起的问题,经同事推荐使用Jmap+MAT的组合进行了一次尝试,下面记录一下,以便今后深入学习使用。

在程序出现内存溢出情况之前,想要观察内存使用情况的话,可以借助atop命令查看内存的使用情况。

这里可以用一个linux下的命令(jps:虚拟机进程状况工具)查看所有Java相关线程的pid等信息。

然后使用jmap(Java内存映像工具)命令,jmap是一个可以输出内存中所有对象的工具,甚至可以将VM中的heap,以二进制输出成文本。jmap-dump:format=b,file=heap.bin 8120可以将8120进程的内存heap输出到heap.bin文件里。它可以打印出某个Java进程(使用pid)内存中所有“对象”的情况(如:产生哪些对象,及其数量)。

再借助MAT(将heap.bin导入到MAT中)生成内存消耗的详细信息。

MAT安装与介绍

下载地址:http://www.eclipse.org/mat/downloads.php。

通过MAT打开dump出来的内存文件,打开后如下图:

Jmap+MAT 排查内存泄漏

从上图可以看到它的大部分功能。

1. Histogram可以列出内存中的对象,对象的个数以及大小。

2. Dominator Tree可以列出那个线程,以及线程下面的那些对象占用的空间。

3.Top consumers通过图形列出最大的object。

4.Leak Suspects通过MA自动分析泄漏的原因。 Histogram如下图: Objects:类的对象的数量。 Shallow size:就是对象本身占用内存的大小,不包含对其他对象的引用,也就是对象头加成员变量(不是成员变量的值)的总和。 Retained size:是该对象自己的shallow size,加上从该对象能直接或间接访问到对象的shallow size之和。换句话说,retained size是该对象被GC之后所能回收到内存的总和。 我们发现ThreadLocal和bingo.persister.dao.Daos类的对象占用了很多空间。

Jmap+MAT 排查内存泄漏

Dominator Tree如下图: 我们发现quartz的定时器的工作线程(10个)占了很多的内存空间

Jmap+MAT 排查内存泄漏

Top consumers如下图: 这里显示了内存中最大的对象有哪些,他们对应的类是哪些,类加载器classloader是哪些。 有些时候,我们在这里就可以看到代码泄露的位置。

Jmap+MAT 排查内存泄漏

Leak Suspects如下图: 从那个饼图,该图深色区域被怀疑有内存泄漏,可以发现整个heap才250M内存,深色区域就占了34%。后面的描述,告诉我们quartz线程占用了大量内存,并指出system class loader加载的"java.lang.ThreadLocal"实例的内存中聚集(消耗空间),并建议用关键字"java.lang.ThreadLocal$ThreadLocalMap$Entry[]"进行检查。所以,MAT通过简单的报告就说明了问题所在。

Jmap+MAT 排查内存泄漏

通过Leak Suspects的 Problem Suspect 1点击【 Details »】, 如下图如下图所示的上下文菜单中选择 List objects -> with outgoning references, 查看ThreadLocal都应用了些什么对象。

Jmap+MAT 排查内存泄漏

现在看到ThreadLocal中引用的对象如下图: 是dao对象 ps:该dao对象包含一个轻量级的ORM关系内容,所以Retained size比较大。

Jmap+MAT 排查内存泄漏

下面继续查看dao的gc ROOT 如下图所示的上下文菜单中选择 Path To GC Roots -> exclude weak references, 过滤掉弱引用,因为在这里弱引用不是引起问题的关键。

Jmap+MAT 排查内存泄漏

从下图中,可以看到在org.quartz.simpl.SimpleThreadPool中保存了daos的引用。所以可以得出是是因为定时器在运行的过程中持有大量的Daos对象应起了内存泄露。为什么会有那么多的Daos呢,Daos不是一个无状态的单例的、可以重用的吗?继续查看spring配置文件发现Daos的bean配置成scope="prototype",导致定时任务又是每次调用都生产新的Daos实例。由于是Daos是无状态的,修改为单例的,问题解决。

Jmap+MAT 排查内存泄漏

以上是通过MAT分析Tomcat应用程序,找到内存泄露的原因,并解决。

继续阅读