Eclipse作为JAVA非常好用的一款IDE,其自带的可扩展插件非常有利于JAVA程序员的工作效率提升。

MemoryAnalyzerTool(也叫MAT)是一款JAVA虚拟机内存映像分析工具,可以在JAVA程序运行的时候有程序抛出的异常加上已经设置好的参数(-XX:+HeapDumpOnOutOfMemory)调试出内存泄漏或者异常的位置以及原因跟踪,MemeoryAnalyzer可以对Dump出来的堆转储快照进行分析,重点是确认内存中的对象是否是有必要的,也就是要先确认到底是出现了内存泄漏(Memory Leak)还是内存溢出(Memory OverFlow)。

二、准备工作

1.Eclipse IDE:我的是安装的Eclipse Neon3,大部分版本Eclipse都支持插件安装。

名称:Eclipse Neon

下载地址: https://www.eclipse.org/downloads/packages/release/neon/3 (我的是Windows64位的JavaEE版本)

2.Memory Analyzer Tool(MAT):由于Eclipse支持直接从URL导入包快捷安装,以及下载压缩包后导入安装,下载包在Eclipse官网中能很快找到:

名称:Memory Analyzer

版本:最新版1.7.0.20170613

官网下载地址: https://eclipse.org/mat/downloads.php

里面有三种:

1.Eclipse导入的URL,特点:安装简单快捷,不需要自己去手动下载,直接输入URL由Eclipse帮你自动下载;

2.Eclispe导入的zip,特点:自己下载,当Eclipse自带工具下载较慢的时候可以使用自己下载然后导入zip即可;

3.独立的MemoryAnalyzer工具,特点:自行下载,不依赖于Eclipse的启动,可直接独立导入.hprof文件进行分析。

三、开始安装MAT

运行Eclipse,然后单击菜单栏上“Help=》Install New Software...”

进入软件源导入界面,你可以选择使用URL直接导入,URL导入的地址为:

http://download.eclipse.org/mat/1.7/update-site/

但是由于镜像连接不稳定性,我使用的是下载压缩包zip然后安装,压缩包下载地址为: http://ftp.jaist.ac.jp/pub/eclipse/mat/1.7/MemoryAnalyzer-1.7.0.201706130745.zip

下载完成之后将其导入,"Add》Archive》选择你下载好的zip文件》OK":

确定之后勾选"Memory Analyzer for Eclipse IDE"下面的两项即可,这个是用于Eclipse内部使用的插件,下面一个“Stand-alone Memory Analyzer“是独立的分析工具,一般不用勾选。

之后单击“Next"等待一段时间Eclipse计算的需求和依赖文件下载完毕后,一路"next",并接受协议“Finish”即可完成安装,重启Eclispe后就可以使用MAT了。

四、MAT的简单使用

1.设置JVM参数

JVM的参数可以用于调试JVM在各种限制条件下的常见BUG,一下简单介绍几个参数:

-XX:+HeapDumpOnOutOfMemoryError    //虚拟机在堆异常时生成对存储快照,后缀一般是.hprof
-Xms    //Java堆的最小值,例如-Xms20m,将Java堆的最小值设置为20MB
-Xmx    //Java堆的最大值,例如-Xms40m,将Java堆的最大值设置为40MB

了解了参数之后,可以对参数进行一定的设置,以便我们简单的对MAT工具进行使用测试,设置参数如下:

-XX:+HeapDumpOnOutOfMemoryError
-Xms20m
-Xmx20m

之所以将最小和最大的堆设置为一样是为了能避免堆的自动扩展,以及20MB的空间是为了让程序能尽快的占满使得内存溢出。

设置JVM的启动参数可在“Run》Run Configuration》Arguments》Vm arguments”中填写

2.编写内存溢出(Memory Leak)代码

思路:通过新建集合列表List,然后以while(true)死循环的方式使得List集合不断添加新的对象,使得其不断地占满堆空间

package com.study;
import java.util.ArrayList;
import java.util.List;
public class TestStudy {
    static class OOMClass { }
    public static void main(String[] args) {
        List<OOMClass> list = new ArrayList<OOMClass>();
        while(true) {
            list.add(new OOMClass());

  执行代码,发现输出如下:

  注意到“java_pid24792.hprof”文件,这个文件是JVM生成的对存储快照,之后可以通过MAT工具打开,进行分析追踪,这个文件将自动保存到你的Eclipse workspace下的工作目录中,以我的为例:

3.打开MAT的Perspective

  由于MAT工具需要另外打开一个工作视图(就像Eclipse EE和Java的切换视图一样),因此需要在先配置打开"Window》Perspective》Open Perspective》Other...":

  选择Memory Analysis并确定:

  完后之后将进入MAT的工作空间视图,你可以在Eclipse右上角进行切换回Java或者EE,由于接下来要使用MAT分析,因此就暂时不切回之前的Java了

4.打开需要分析的堆转储快照.hprof文件

  由于MAT是依赖于生成的堆转储快照,而不是在运行的时候自动捕获,因此,再确认进入MAT的工作空间后,点击"File》Open Heap Dump》选择刚生成的“java_pid24792.hprof”文件,一直默认就可以查看内存问题的分析图表以及GC根

  你可以进行一些分析操作,可以看到Action下的几个选项:

  a.Histogram:列出每个类的实例列表;

  b.Dominator:列出最大的对象和他们存在的东西;

  c.Top Comsumer:打印按类和包分组的最昂贵的对象;

  d.Duplicate Classes:检测由多个类加载器加载的类。

5.使用MAT找出问题存在的对象

  由于是一个简单的问题,因此我们直接选择Dominator,可以直接以百分比的形式打印出对象所占的百分比,一般的内存溢出大部分情况都是有某一个对象过多存在而未被GC回收导致,所以可以点开最大百分比的类一级级找,不难找到问题所在:

  可以看到列出额其中的25条,还有810300条目未列出,这81万个对象占据了97.36%的堆空间,因此不难发现是OOMClass类的对象生成过多为未被回收导致内存溢出。

6.进一步追踪问题源并进行定位

  在"default_report org.eclipse.mat.api:suspects"的标签分页中

  在"Program Suspect 1》Details"点击后找到"Thread Stack"中追踪出错点

  看到出错点在第13行,找到对应的Java代码TestStudy的13行:

 1 package com.study;
 3 import java.util.ArrayList;
 4 import java.util.List;
 6 public class TestStudy {
 8     static class OOMClass { }
10     public static void main(String[] args) {
11         List<OOMClass> list = new ArrayList<OOMClass>();
12         while(true) {
13             list.add(new OOMClass());
14         }
15     }

  找到了追踪的出错位置,以上就是使用Memory Analyzer工具完成的堆栈分析。

作者:letcafe