内存分析插件 MAT

Eclipse MAT 则是优秀的内存对象分析开源工具 . 它们对于分析内存溢出问题非常有用。

MAT支持两种安装方式,一种是“独立版本”,用户不必安装 EclipseIDE 环境,MAT 作为一个独立的 EclipseRCP 应用运行;另一种是“插件版本”,也就是说MAT 可以作为 EclipseIDE 的一个插件,和Eclipse开发平台集成。

第一部分:安装篇

myEclipse 

步骤:

第一步:装一个 Eclipse 的内存分析插件 MAT

http://download.eclipse.org/technology/mat/latest/update-site/  

http://www.eclipse.org/mat/downloads.php

 

注意:图上所标的是插件板下载地址,同时也可以下载rcp版本进行独立安装。 

 

第二步:下载之后将压缩包解压,放置到Myeclipse 的\MyEclipse 9\dropins目录下

解压后会看见这些文件: 纠正下面一个错别字不是"写"是 "下"

 

第三步:按照下图,A,B,C的顺序进行对应的文件的创建

 

第四部:重启Myeclipse,打开Window->Perferences,你会看见这个项目:

 

 

简单的说一下使用(控制台的)如果是tomcat或者是别的服务器需要你去查如何配置JVM参数:

以下是一个会导致java.lang.OutOfMemoryError: Java heap space的程序代码:(very easy)

map=new HashMap(); for (int i = 0; i < 600000000; i++) { map.put(i, new Date()); } } }

首先在运行之前有一些参数需要设置:

 

然后就到了参数设置的页面,按照A,B的顺序设置参数:(-XX:+HeapDumpOnOutOfMemoryError)避免写错误可以copy

 

运行错误的程序代码会看见以下结果:

 

那么这时候就生成了一个文件java_pid3708.hprof,这个文件 在你的项目的根目录下(myeclipse10)

 

那么接下来我们就打开这个文件进行分析如何打开见下图:(选中刚刚在项目根目录下生成的文件java_pid3708.hprof打开)

 

打开之后你会看见下图就OK了:

 

Ecplise工具

第一步:下载安装

可以直接下载mat插件安装,也可以在工具help中查询进行安装。

  1. 打开Eclipse - >help - > Eclipse Marketplace

     
  2.  

    点击install,等待下面的进度条加载完毕后,勾选全部,点击Next

     
  3.  

    同意协议后,点击Finish就开始安装MAT了

     
     

直接输入URL安装 

  1.  

     打开Eclipse - >help - > Install New Software

  2.  

     在work with输入图中下载地址,勾选Memory  Analyzer for Eclipse IDE选项

  3.  若没有勾选Memory  Analyzer for Eclipse IDE选项,点击地址栏旁边的Add,在location里输入以上地址,点击OK即可。

第二步:切换到 Memory Analysis 模式 

第三步:通过 File > Open Heap Dump.... 查看 dump 出来的文件

 

 

 

与eclipse IDE集成安装过程,可参看以下文章:

http://www.ibm.com/developerworks/cn/opensource/os-cn-ecl-ma/index.html

 

比较而言,单机版的安装方式非常简单,用户只需要下载相应的安装包,然后解压缩即可运行,这也是被普遍采用的一种安装方式。在下面的例子里,我们使用的也是单机版的 MAT。具体的下载要求和地址可参见其产品下载页面:http://www.eclipse.org/mat/downloads.php

另外,如果你需要用 MAT 来分析 IBM JVM 生成的 dump 文件的话,还需要额外安装 IBM Diagnostic Tool Framework ,具体的下载和安装配置步骤请参见:http://www.ibm.com/developerworks/java/jdk/tools/dtfj.html

 

Eclipse Memory Analyzer是一个非常好用的内存dump文件分析工具,我们可以利用它的Eclipse 插件轻松实现查看对象树、对象大小、生成报告,甚至自动化分析可能出现泄露的对象。关于MAT的使用介绍可以参考:http://www.ibm.com/developerworks/cn/opensource/os-cn-ecl-ma/index.html?ca=drs-。 文章中的例子是在windows平台下分析,对于非常大的dump文件就无能为力了。

 

Linux下使用MAT
对于非常大的dump文件MAT同样有办法分析,有下面几个步骤:
Step 1:下载MAT的Stand-alone Eclipse RCP Applications,找一台足够大内存的linux机器,将MAT复制上去。

Step 2:进入mat所在目录,编辑MemoryAnalyzer.ini文件设置最大内存值比如-Xmx9g。

Step 3:执行./ParseHeapDump.sh xxxx.bin 来分析dump文件,MAT的分析速度还是很快的。最终得到以下文件。

 

./ParseHeapDump.sh heap.bin org.eclipse.mat.api:suspects org.eclipse.mat.api:overview org.eclipse.mat.api:top_components

会得到suspects, overview和top_components三个视图的信息。

Step 4:将分析得到的文件包括原dump文件下载回windows平台,打开eclipse插件使用菜单File–>Open Heap Dump打开dump文件即可查看到分析结果。

 

配置环境参数

安装完成后,为了更有效率的使用 MAT,我们可以配置一些环境参数。因为通常而言,分析一个堆转储文件需要消耗很多的堆空间,为了保证分析的效率和性能,在有条件的情况下,我们会建议分配给 MAT 尽可能多的内存资源。你可以采用如下两种方式来分配内存更多的内存资源给 MAT。

一种是修改启动参数 MemoryAnalyzer.exe-vmargs -Xmx4g

另一种是编辑文件 MemoryAnalyzer.ini,在里面添加类似信息 -vmargs– Xmx4g。

 

 

说明:

1. MemoryAnalyzer.ini中的参数一般默认为-vmargs– Xmx1024m,这就够用了。假如你机器的内存不大,改大该参数的值,会导致MemoryAnalyzer启动时,报错:Failed to create the Java Virtual Machine。

2.当你导出的dump文件的大小大于你配置的1024m(说明1中,提到的配置:-vmargs– Xmx1024m),MAT输出分析报告的时候,会报错:An internal error occurred during: "Parsing heap dump from XXX”。适当调大说明1中的参数即可。

可以参考官网给出的处理方式:

Alternatively, edit the MemoryAnalyzer.ini to contain:
-vmargs
-Xmx2g
-XX:-UseGCOverheadLimit

 

>>我的使用情况:
一个3.2G的dump文件,使用jvisualvm打开用了30分钟,且查看类的某实例时打开时间也很久。
使用MAT插件,总是报不能解析,调整xmx为1024M后仍不能,win7 32位分配内存太大会报不开。
使用RCP版,默认配置xmx=1024M也未能打开,调整成1300M后解析内存使用情况。????

MAT是分32位与64位的,32位最大内存2g,再大就得用64位的了。

 

 

获取堆转储文件

只要你设置了如下所示的 JVM 参数:

-XX:+HeapDumpOnOutOfMemoryError

JVM 就会在发生内存泄露时抓拍下当时的内存状态,也就是我们想要的堆转储文件。

如果你不想等到发生崩溃性的错误时才获得堆转储文件,也可以通过设置如下 JVM 参数来按需获取堆转储文件。

-XX:+HeapDumpOnCtrlBreak

除此之外,还有很多的工具,例如 JMap,JConsole 都可以帮助我们得到一个堆转储文件。本文实例就是使用 JMap 直接获取了 Eclipse Galileo 进程的堆转储文件。您可以使用如下命令:

JMap -dump:format=b,file=<dumpfile> <pid>

不过,您需要了解到,不同厂家的 JVM 所生成的堆转储文件在数据存储格式以及数据存储内容上有很多区别, MAT 不是一个万能工具,它并不能处理所有类型的堆存储文件。但是比较主流的厂家和格式,例如 Sun, HP, SAP 所采用的 HPROF 二进制堆存储文件,以及 IBM 的 PHD 堆存储文件等都能被很好的解析(您需要安装额外的插件,请参考 相关说明,本文不作详细解释)。

万事俱备,接下来,我们就可以开始体验一键式的堆存储分析功能了。

 

转储方式获取,可参考下:http://www.ibm.com/developerworks/cn/websphere/techjournal/1103_supauth/1103_supauth.html

 

 

第二部分 使用篇

官方文档:http://help.eclipse.org/luna/index.jsp?topic=/org.eclipse.mat.ui.help/welcome.html

首先,启动前面安装配置好的 Memory Analyzer tool , 然后选择菜单项 File- Open Heap Dump 来加载需要分析的堆转储文件。文件加载完成后,你可以看到如图 所示的界面:

图 4. 概览

通过上面的概览,我们对内存占用情况有了一个总体的了解。

在Eclipse的左上角有Open Heap Dump按钮,按照刚才说的路径找到堆转储文件并打开。

先检查一下 MAT生成的一系列文件:(截图来自另一个例子)

可以看到 MAT工具提供了一个很贴心的功能,将报告的内容压缩打包到一个 zip文件,并把它存放到原始堆转储文件的存放目录下,这样如果您需要和同事一起分析这个内存问题的话,只需要把这个小小的 zip包发给他就可以了,不需要把整个堆文件发给他。并且整个报告是一个 HTML格式的文件,用浏览器就可以轻松打开。

使用MAT打开dump文件,等待一会后,会弹出向导界面,保持默认设置,直接点Finish即是分析内存泄露问题。在点击Finish后,会出现overview界面,您可以点击工具栏上的 Leak Suspects 菜单项来生成内存泄露分析报告,也可以直接点击饼图下方的 Reports->Leak Suspects链接来生成报告。如图:


MAT工具分析了heap dump后在界面上非常直观的展示了一个饼图,该图深色区域被怀疑有内存泄漏,可以发现整个heap才64M内存,深色区域就占了99.5%。接下来是一个简短的描述,告诉我们main线程占用了大量内存,并且明确指出system class loader加载的"java.lang.Thread"实例有内存聚集,并建议用关键字"java.lang.Thread"进行检查。所以,MAT通过简单的两句话就说明了问题所在,就算使用者没什么处理内存问题的经验。在下面还有一个"Details"链接,在点开之前不妨考虑一个问题:为何对象实例会聚集在内存中,为何存活(而未被GC)?是的——Strong Ref,那么再走近一些吧。如图:


点击了"Details"链接之后,除了在上一页看到的描述外,还有Shortest Paths To the Accumulation Point和Accumulated Objects部分,这里说明了从GC root到聚集点的最短路径,以及完整的reference chain。观察Accumulated Objects部分,java.util.HashMap和java.lang.Object[1000000]实例的retained heap(size)最大,在上一篇文章中我们知道retained heap代表从该类实例沿着reference chain往下所能收集到的其他类实例的shallow heap(size)总和,所以明显类实例都聚集在HashMap和Object数组中了。这里我们发现一个有趣的现象,既Object数组的shallow heap和retained heap竟然一样,通过Shallow and retained sizes一文可知,数组的shallow heap和一般对象(非数组)不同,依赖于数组的长度和里面的元素的类型,对数组求shallow heap,也就是求数组集合内所有对象的shallow heap之和。好,再来看org.rosenjiang.bo.Pilot对象实例的shallow heap为何是16,因为对象头是8字节,成员变量int是4字节、String引用是4字节,故总共16字节。

 

在Accumulated Objects视图中,retained heap占用最多的是hashMap和object数组,为啥它们会占用这么大的heap呢?这个时候需要分析hashMap和object数组中存放了一些什么对象?接着往下看,来到了Accumulated Objects by Class区域,顾名思义,这里能找到被聚集的对象实例的类名。org.rosenjiang.bo.Pilot类上头条了,被实例化了290,325次,再返回去看程序,我承认是故意这么干的。还有很多有用的报告可用来协助分析问题,只是本文中的例子太简单,也用不上。



为了更多的了解MAT的功能,再举一些例子(不提供对应的代码):

 

有问题的对象,可以看到akka.dispatch.Dispatcher$$anon$1一个实例占用了2.4GB的内存,这就是罪魁祸首。这其实是akka dispatcher的mailbox中的java.util.concurrent.ConcurrentLinkedQueue,每个Node占用了81M的内存,消息体太大了。

例子二:

通过MAT发现heap dump问题所在,就需要寻找导致内存泄漏的代码点。这时往往需要打开对象依赖关系树形视图,点击如图按钮即可。 

这时会看到如下视图:

这个视图的右边大区域可以看到对象的依赖关系,选中某个对象以后可以在左边小窗口查看对象的一些属性。如果属性的值是一些内存地址你还可以点击工具栏的搜索按钮来搜索具体的对象信息。在进行具体分析的时候MAT只是起了帮助你进行分析的工具的功能,OOM问题分析没有固定方法和准则。只能发挥你敏锐的洞察力,结合源代码,对内存中的对象进行分析从而找到代码中的BUG. 

 

切到histogram视图,按shadow heap降序排列。

比如,选取byte数组,右击->list objects->with incoming references,降序排列可以看到有很多大小一致的byte[]实例。

右击其中一个数组->Path to GC Roots-> exclude xxx references

这样也可以打开上图

 

多个dump文件,也可以在NavigationHistory view里(如果看不到就从Window > Navigation History找 ),右击histogram然后选择Add to Compare Basket ,可以实现多个文件直方图对比。更加直观。

 

例子三:

如何查看某一个对象占用的内存空间 
1.按以下方式打开新窗口即可 ,如图:

2.输入类名(输入类的全名) ,如图:

 

 

 

 

 

第三部分 分析三步曲

通常我们都会采用下面的“三步曲”来分析内存泄露问题:

首先,对问题发生时刻的系统内存状态获取一个整体印象。

第二步,找到最有可能导致内存泄露的元凶,通常也就是消耗内存最多的对象

接下来,进一步去查看这个内存消耗大户的具体情况,看看是否有什么异常的行为。

下面将用一个基本的例子来展示如何采用“三步曲”来查看生产的分析报告。

查看报告之一:内存消耗的整体状况

图 7. 内存泄露分析报告

如图所示,在报告上最醒目的就是一张简洁明了的饼图,从图上我们可以清晰地看到一个可疑对象消耗了系统 99% 的内存。

在图的下方还有对这个可疑对象的进一步描述。我们可以看到内存是由 java.util.Vector 的实例消耗的,com.ibm.oti.vm.BootstrapClassLoader 负责这个对象的加载。这段描述非常短,但我相信您已经可以从中找到很多线索了,比如是哪个类占用了绝大多数的内存,它属于哪个组件等等。

接下来,我们应该进一步去分析问题,为什么一个 Vector 会占据了系统 99% 的内存,谁阻止了垃圾回收机制对它的回收。

查看报告之二:分析问题的所在

首先我们简单回顾下 JAVA 的内存回收机制,内存空间中垃圾回收的工作由垃圾回收器 (Garbage Collector,GC) 完成的,它的核心思想是:对虚拟机可用内存空间,即堆空间中的对象进行识别,如果对象正在被引用,那么称其为存活对象,反之,如果对象不再被引用,则为垃圾对象,可以回收其占据的空间,用于再分配。

在垃圾回收机制中有一组元素被称为根元素集合,它们是一组被虚拟机直接引用的对象,比如,正在运行的线程对象,系统调用栈里面的对象以及被 system class loader 所加载的那些对象。堆空间中的每个对象都是由一个根元素为起点被层层调用的。因此,一个对象还被某一个存活的根元素所引用,就会被认为是存活对象,不能被回收,进行内存释放。因此,我们可以通过分析一个对象到根元素的引用路径来分析为什么该对象不能被顺利回收。如果说一个对象已经不被任何程序逻辑所需要但是还存在被根元素引用的情况,我们可以说这里存在内存泄露。

现在,让我们开始真正的寻找内存泄露之旅,点击“Details ”链接,可以看到如图 所示对可疑对象 1 的详细分析报告。

图 8. 可疑对象 1 的详细分析报告
  1. 我们查看下从 GC 根元素到内存消耗聚集点的最短路径:
图  从根元素到内存消耗聚集点的最短路径

图 9. 从根元素到内存消耗聚集点的最短路径

我们可以很清楚的看到整个引用链,内存聚集点是一个拥有大量对象的集合,如果你对代码比较熟悉的话,相信这些信息应该能给你提供一些找到内存泄露的思路了。

接下来,我们再继续看看,这个对象集合里到底存放了什么,为什么会消耗掉如此多的内存。

图 内存消耗聚集对象信息

图 10. 内存消耗聚集对象信息

在这张图上,我们可以清楚的看到,这个对象集合中保存了大量 Person 对象的引用,就是它导致的内存泄露。

至此,我们已经拥有了足够的信息去寻找泄露点,回到代码,我们发现,是下面的代码导致了内存泄露 :

清单 1. 内存泄漏的代码段
 while (1<2) 
 { 
			
	 Person person = new Person("name","address",i); 
	 v.add(person); 
	 person = null; 
 }
 

分析资料:

1、Finalizer对象 JVM OOM & JAVA finalizer 引发的OOM & Thread.stopjava.lang.ref.FinalizerReference引发的内存泄漏

2、

 

实例:内存快照排查OOM,加密时错误方法指定provider方式错误引起的OOM

 

 

参考资料:

http://wiki.eclipse.org/index.php/MemoryAnalyzer
使用 Eclipse Memory Analyzer 进行堆转储文件分析
Eclipse Memory Analyser (MAT) - Tutorial
使用Memory Analyzer tool(MAT)分析内存泄漏(一)
使用Memory Analyzer tool(MAT)分析内存泄漏(二)

使用 Eclipse Memory Analyzer 进行堆转储文件分析

http://blog.csdn.net/rachel_luo/article/details/8992461

memoryanalyzer Blog

java类加载器体系结构
ClassLoader

Android OOM案例分析

Java程序内存分析:使用mat工具分析内存占用

 

官方文档:
http://help.eclipse.org/luna/index.jsp?topic=/org.eclipse.mat.ui.help/welcome.html

jrocket的dump操作:
http://blog.csdn.net/chenyi8888/article/details/8488727

 

posted @ 2014-03-26 16:19  milkty  阅读(3369)  评论(0编辑  收藏  举报