MAT java dump分析工具
JVM Heap Dump记录了jvm中堆内存使用情况,
特点:有堆内存使用情况,有线程堆栈信息,数据源配置、对象、方法局部变量等都在dump中有。
目前使用的工具是Eclipse Memory Analyzer(MAT)。
下载与安装
Eclipse Memory Analyzer(MAT)支持两种安装方式,一是Eclipse插件的方式,另外一个就是独立运行的方式,建议使用独立运行的方式。
在 http://www.eclipse.org/mat/downloads.php 下载安装MAT,启动之后打开 File - Open Heap Dump... 菜单,然后选择生成的Heap DUmp文件,选择 "Leak Suspects Report",然后点击 "Finish" 按钮。
接下来介绍界面中常用到的功能:
Overview
Overview视图,即概要界面,显示了概要的信息,并展示了MAT常用的一些功能。
- Details 显示了一些统计信息,包括整个堆内存的大小、类(Class)的数量、对象(Object)的数量、类加载器(Class Loader)的数量。
- Biggest Objects by Retained Size 使用饼图的方式直观地显示了在JVM堆内存中最大的几个对象,当光标移到饼图上的时候会在左边Inspector和Attributes窗口中显示详细的信息。
- Actions 这里显示了几种常用到的操作,算是功能的快捷方式,包括 Histogram、Dominator Tree、Top Consumers、Duplicate Classes,具体的含义和用法见下面;
- Reports 列出了常用的报告信息,包括 Leak Suspects和Top Components,具体的含义和内容见下;
- Step By Step 以向导的方式引导使用功能。
Histogram
直方图,可以查看每个类的实例(即对象)的数量和大小。
Dominator Tree
支配树,列出Heap Dump中处于活跃状态中的最大的几个对象,默认按 retained size进行排序,因此很容易找到占用内存最多的对象。
OQL
MAT提供了一个对象查询语言(OQL),跟SQL语言类似,将类当作表、对象当作记录行、成员变量当作表中的字段。通过OQL可以方便快捷的查询一些需要的信息,是一个非常有用的工具。
Thread Overview
此工具可以查看生成Heap Dump文件的时候线程的运行情况,用于线程的分析。通过线程看调用堆栈上,各个阶段的对象、局部变量等情况
Run Expert System Test
可以查看分析完成的HTML形式的报告,也可以打开已经产生的分析报告文件,子菜单项如下图所示:
常用的主要有Leak Suspects和Top Components两种报告:
- Leak Suspects 可以说是非常常用的报告了,该报告分析了 Heap Dump并尝试找出内存泄漏点,最后在生成的报告中对检测到的可疑点做了详细的说明;
- Top Components 列出占用总堆内存超过1%的对象。
Open Query Browser
提供了在分析过程中用到的工具,通常都集成在了右键菜单中,在后面具体举例分析的时候会做详细的说明。如下图:
这里仅针对在 Overview 界面中的 Acations中列出的两项进行说明:
- Top Consumers 按类、类加载器和包分别进行查询,并以饼图的方式列出最大的几个对象。菜单打开方式如下:
- Duplicate Classes 列出被加载多次的类,结果按类加载器进行分组,目标是加载同一个类多次被类加载器加载。使用该工具很容易找到部署应用的时候使用了同一个库的多个版本。菜单打开方式如下图:
Find Object by address
通过十六进制的地址查找对应的对象,见下图:
基础概念
先列出几个基础的概念:
Shallow Heap 和 Retained Heap
Shallow Heap表示对象本身占用内存的大小,不包含对其他对象的引用,也就是对象头加成员变量(不是成员变量的值)的总和。
Retained Heap是该对象自己的Shallow Heap,并加上从该对象能直接或间接访问到对象的Shallow Heap之和。换句话说,Retained Heap是该对象GC之后所能回收到内存的总和。
把内存中的对象看成下图中的节点,并且对象和对象之间互相引用。这里有一个特殊的节点GC Roots,这就是reference chain的起点。
从obj1入手,上图中蓝色节点代表仅仅只有通过obj1才能直接或间接访问的对象。因为可以通过GC Roots访问,所以左图的obj3不是蓝色节点;而在右图却是蓝色,因为它已经被包含在retained集合内。所以对于左图,obj1的retained size是obj1、obj2、obj4的shallow size总和;右图的retained size是obj1、obj2、obj3、obj4的shallow size总和。obj2的retained size可以通过相同的方式计算。
对象引用(Reference)
对象引用按从最强到最弱有如下级别,不同的引用(可到达性)级别反映了对象的生命周期:
- 强引用(Strong Ref):通常我们编写的代码都是强引用,于此相对应的是强可达性,只有去掉强可达性,对象才能被回收。
- 软引用(Soft Ref):对应软可达性,只要有足够的内存就一直保持对象,直到发现内存不足且没有强引用的时候才回收对象。
- 弱引用(Weak Ref):比软引用更弱,当发现不存在强引用的时候会立即回收此类型的对象,而不需要等到内存不足。通过java.lang.ref.WeakReference和java.util.WeakHashMap类实现。
- 虚引用(Phantom Ref):根本不会在内存中保持该类型的对象,只能使用虚引用本身,一般用于在进入finalize()方法后进行特殊的清理过程,通过java.lang.ref.PhantomReference实现。
GC Roots和Reference Chain
JVM在进行GC的时候是通过使用可达性来判断对象是否存活,通过GC Roots(GC根节点)的对象作为起始点,从这些节点开始进行向下搜索,搜索所走过的路径成为Reference Chain(引用链),当一个对象到GC Roots没有任何引用链相连(用图论的话来说就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。
如下图所示,对象object 5、object 6、object 7虽然互相有关联,它们的引用并不为0,但是它们到GC Roots是不可达的,因此它们将会被判定为是可回收的对象。
Histogram(直方图)视图
点击工具栏上的 图标可以打开Histogram(直方图)视图,可以列出每个类产生的实例数量,以及所占用的内存大小和百分比。主界面如下图所示:
图中Shallow Heap 和 Retained Heap分别表示对象自身不包含引用的大小和对象自身并包含引用的大小,具体请参考下面 Shallow Heap 和 Retained Heap 部分的内容。默认的大小单位是 Bytes,可以在 Window - Preferences 菜单中设置单位,图中设置的是KB。
通过直方图视图可以很容易找到占用内存最多的几个类(通过Retained Heap排序),还可以通过其他方式进行分组(见下图)。
如果存在内存溢出,时间久了溢出类的实例数量或者内存占比会越来越多,排名也越来越靠前。可以点击工具类上的 图标进行对比,通过多次对比不同时间点下的直方图对比就很容易把溢出的类找出来。
还有一种对比直方图的方式,首先通过 Window 菜单打开 Navigation History 视图,选中直方图右键并选中 Add to Compare Basket项目,将直方图添加到 Compare Basket 中。
然后在 Compare Basket 中点击右上角的 按钮,可以分别列出对比的所有结果,见下图:
并且在上面的可以设置不同的对比方式。
Dominator Tree视图
点击工具栏上的 图标可以打开Dominator Tree(支配树)视图,在此视图中列出了每个对象(Object Instance)与其引用关系的树状结构,同时包含了占用内存的大小和百分比。
通过Dominator Tree视图可以很容易的找出占用内存最多的几个对象(根据Retained Heap或Percentage排序),和Histogram类似,可以通过不同的方式进行分组显示:
定位溢出源
Histogram视图和Dominator Tree视图的角度不同,前者是基于类的角度,后者是基于对象实例的角度,并且可以更方便的看出其引用关系。
首先,在两个视图中找出疑似溢出的对象或者类(可以通过Retained Heap排序,并且可以在Class Name中输入正则表达式的关键词只显示指定的类名),然后右键选择Path To GC Roots(Histogram中没有此项)或Merge Shortest Paths to GC Roots,然后选择 exclude all phantom/weak/soft etc. reference:
GC Roots意为GC根节点,其含义见上面的 GC Roots和Reference Chain 部分,后面的 exclude all phantom/weak/soft etc. reference 意思是排除虚引用、弱引用和软引用,即只剩下强引用,因为除了强引用之外,其他的引用都可以被JVM GC掉,如果一个对象始终无法被GC,就说明有强引用存在,从而导致在GC的过程中一直得不到回收,最终就内存溢出了。
通过结果就可以很方便的定位到具体的代码,然后分析是什么原因无法释放该对象,比如被缓存了或者没有使用单例模式等等。
下面是执行的结果:
上图中保留了大量的VelocitySqlBulder的外部引用,后来查看了代码,原来每次调用的时候都实例化一个新的对象,由于VelocitySqlBulder类是无状态的工具类,因此修改为单例方式就可以解决这个问题。
补充说明:
有时dump文件很大,但是分析的Overview没有那么大,那是有些不可达的对象被忽略了、
Details展示中的"Unreachable Objects Histogram"?直接到eclipse翻阅文档,看看什么叫Unreachable Objects(原文在这里)。大致意思是说:
有时一个堆dump文件包含了可能会在下一次GC时被清理掉的对象,站在可达性分析的角度来说,这些对象没有和GC Roots关联,MAT认为它们对于分析内存问题并不重要,所以默认会在解析dump文件时将它们删除。如果dump文件是由OOM时自动产生的,那么其中一般不会包含这些对象,因为JVM通常会执行GC以尝试释放空间;但是也不一定,比如要新分配的对象太大,JVM无法释放足够的空间;或者我们自己手动dump时未使用live参数。
来自 <http://www.voycn.com/index.php/article/matfenxidumpwenjianxianshidaxiaobijmapchaxunjieguoxiao>
想MAT在分析dump时不删除Unreachable Objects,修改一下配置即可(当然前提是dump文件中本就包含这些对象)。
1、在Window->Preferences->Memory Analyzer中选中"Keep unreachable objects",然后点击"Apply and Close"保存配置
2、关闭已打开的dump文件,×掉就可以了
3、进入Window->Heap Dump History ,选择分析的dump文件,然后右键删除索引文件:Delete Index Files。MAT分析dump后会生成很多索引文件,如果不删除这些文件,MAT就不会重新解析。 可以在这里删除,也可以自行到dump文件所在目录手动删除。
4、然后选择File,重新打开dump文件,可以看到Size和预期一致了,不可达的对象没有被删除,然后就可以继续分析了:
5、选择Open Query Browser->Java Basics->GC Roots
在gc_roots中选中Unreachable栏位,右键选择"Show Retained Set"
- 点赞
- 收藏
- 关注作者
评论(0)