浅尝Eclipse Memory Analyzer(MAT)

前言

在平时开发、测试过程当中、甚至是生产环境中,有时会遇到OutOfMemoryError,Java堆溢出了,这代表程序有严重的问题。咱们须要找形成OutOfMemoryError缘由。通常有两种状况:php

一、内存泄露,对象已经死了,没法经过垃圾收集器进行自动回收,经过找出泄露的代码位置和缘由,才好肯定解决方案;
二、内存溢出,内存中的对象都还必须存活着,这说明Java堆分配空间不足,检查堆设置大小(-Xmx与-Xms),检查代码是否存在对象生命周期太长、持有状态时间过长的状况。
以上是处理Java堆问题的思路,具体是怎么进行分析,这里介绍的是使用Eclipse Memory Analyzer tool(MAT)工具分析的过程。java


生成dump文件

经过jvm参数--XX:-HeapDumpOnOutOfMemoryError可让JVM在出现内存溢出是Dump出当前的内存转储快照;
或者,用jmap生产dump文件,win经过任务管理器查看tomcat的进程pid,linux用ps命令查看进程pid,而后用jmap命令(Java5:jmap -heap:format=b <pid>;Java6:jmap -dump:format=b,file=HeapDump.bin <pid>)。

我这里使用的是,我一辈子产环境项目,运行一段时间大概3周的样子,就会报OutOfMemoryError。(ps:这个项目出现这种状况已经有好长一段时间了,咱们以前的作法是按期的重启tomcat,没有去分析它的缘由。)JDK64位主要参数:-Xmx3078M -Xms3078M -XX:PermSize=1024M -XX:MaxPermSize=1024M,内存仍是蛮大的。linux


MAT安装与介绍
下载地址:http://www.eclipse.org/mat/downloads.php。
经过MAT打开dump出来的内存文件,打开后以下图:spring




从上图能够看到它的大部分功能。
1. Histogram能够列出内存中的对象,对象的个数以及大小。
2. Dominator Tree能够列出那个线程,以及线程下面的那些对象占用的空间。
3.Top consumers经过图形列出最大的object。
4.Leak Suspects经过MA自动分析泄漏的缘由。tomcat

Histogram以下图:eclipse

Objects:类的对象的数量。jvm

Shallow size:就是对象自己占用内存的大小,不包含对其余对象的引用,也就是对象头加成员变量(不是成员变量的值)的总和。工具

Retained size:是该对象本身的shallow size,加上从该对象能直接或间接访问到对象的shallow size之和。换句话说,retained size是该对象被GC以后所能回收到内存的总和。测试

咱们发现ThreadLocal和bingo.persister.dao.Daos类的对象占用了不少空间。spa




Dominator Tree以下图:

咱们发现quartz的定时器的工做线程(10个)占了不少的内存空间



Top consumers以下图:

这里显示了内存中最大的对象有哪些,他们对应的类是哪些,类加载器classloader是哪些。

有些时候,咱们在这里就能够看到代码泄露的位置。




Leak Suspects以下图:

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




经过Leak Suspects的Problem Suspect 1点击【Details »】,

以下图以下图所示的上下文菜单中选择 List objects -> with outgoning references, 查看ThreadLocal都应用了些什么对象。





如今看到ThreadLocal中引用的对象以下图:

是dao对象

ps:该dao对象包含一个轻量级的ORM关系内容,因此Retained size比较大




下面继续查看dao的gc ROOT

以下图所示的上下文菜单中选择 Path To GC Roots -> exclude weak references, 过滤掉弱引用,由于在这里弱引用不是引发问题的关键。



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




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

相关文章
相关标签/搜索