JVM性能分析工具之-- Eclipse Memory Analyzer tool(MAT)

Posted 程序猿技术大咖

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM性能分析工具之-- Eclipse Memory Analyzer tool(MAT)相关的知识,希望对你有一定的参考价值。

前言

在本文中,将介绍MAT如何根据heapdump分析泄漏根源。由于测试范例可能过于简单,很容易找出问题,但我期待借此举一反三。

一开始不得不说说ClassLoader,本质上,它的工作就是把磁盘上的类文件读入内存,然后调用Java.lang.ClassLoader.defineClass方法告诉系统把内存镜像处理成合法的字节码。Java提供了抽象类ClassLoader,所有用户自定义类装载器都实例化自ClassLoader的子类。systemclass loader在没有指定装载器的情况下默认装载用户类,在Sun Java 1.5中既sun.misc.Launcher$AppClassLoader。更详细的内容请参看下面的资料。

准备heap dump
请看下面的Pilot类,没啥特殊的。

/**
 * Pilot class
 * @author rosen jiang
 */
package org.rosenjiang.bo;

public class Pilot{
    
    String name;
    int age;
    
    public Pilot(String a, int b){
        name = a;
        age = b;
    }
}


然后再看OOMHeapTest类,它是如何撑破heapdump的。

/**
 * OOMHeapTest class
 * @author rosen jiang
 */
package org.rosenjiang.test;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.rosenjiang.bo.Pilot;

public class OOMHeapTest {
    public static void main(String[] args){
        oom();
    }
    
    private static void oom(){
        Map<String, Pilot> map = new HashMap<String, Pilot>();
        Object[] array = new Object[1000000];
        for(int i=0; i<1000000; i++){
            String d = new Date().toString();
            Pilot p = new Pilot(d, i);
            map.put(i+"rosen jiang", p);
            array[i]=p;
        }
    }
}


是的,上面构造了很多的Pilot类实例,向数组和map中放。由于是StrongRefGC自然不会回收这些对象,一直放在heap中直到溢出。当然在运行前,先要在Eclipse中配置VM参数-XX:+HeapDumpOnOutOfMemoryError。好了,一会儿功夫内存溢出,控制台打出如下信息。

java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid3600.hprof 
Heap dump file created [78233961 bytes in 1.995 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space



java_pid3600.hprof
既是heap dump,可以在OOMHeapTest类所在的工程根目录下找到。

 

MAT安装

话分两头说,有了heap dump还得安装MAT

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

下载的zip包,解压即可使用。下载页图示如下:


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

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

安装完成后,为了更有效率的使用 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中的参数即可。


至此,MAT 就已经成功地安装配置好了,Eclipse的左上角有Open Heap Dump按钮,按照刚才说的路径找到java_pid3600.hprof文件并打开

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

JVM性能分析工具之-- Eclipse Memory Analyzer tool(MAT)(二)

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

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

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

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

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

JVM性能分析工具之-- Eclipse Memory Analyzer tool(MAT)(二)

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

例子二:

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

JVM性能分析工具之-- Eclipse Memory Analyzer tool(MAT)(二)

这时会看到如下视图:

JVM性能分析工具之-- Eclipse Memory Analyzer tool(MAT)(二)


例子三:

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


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


以上是关于JVM性能分析工具之-- Eclipse Memory Analyzer tool(MAT)的主要内容,如果未能解决你的问题,请参考以下文章

JVM学习十二:JVM之性能监控工具

JVM监控诊断之工具使用(下篇)

JVM监控诊断之工具使用(下篇)

JVM监控诊断之工具使用(下篇)

JVM性能调优-小测试

JVM监控诊断之工具使用(下篇)