1.前言

熟练掌握MAT是Java高手的必备能力,但实践时大家往往需面对众多功能,眼花缭乱不知如何下手,小编也没有找到一篇完善的教学素材,所以整理本文帮大家系统掌握MAT分析工具。

本文详细讲解MAT众多内存分析工具功能,这些功能组合使用异常强大,熟练使用几乎可以解决所有的堆内存离线分析的问题。我们将功能划分为4类:内存分布详情、对象间依赖、对象状态详情、按条件检索。每大类有多个功能点,本文会逐一讲解各功能的场景及用法。此外,添加了原创或引用案例加强理解和掌握。

如图所示:

打开网易新闻 查看精彩图片

为减少对眼花缭乱的菜单的迷茫,可以通过下图先整体熟悉下各功能使用入口,后续都会讲到。

打开网易新闻 查看精彩图片

2.内存分布详解及实战

2.1全局信息概览

功能

:展现堆内存大小、对象数量、class数量、classloader数量、GCRoot数量、环境变量、线程概况等全局统计信息。

使用入口

:MAT主界面→HeapDumpOverview。

举例

:下面是对象数量、classloader数量、GCRoot数量,可以看出classloader存在异常。

打开网易新闻 查看精彩图片

举例

:下图是线程概况,可以查看每个线程名、线程的RetainedHeap、daemon属性等。

打开网易新闻 查看精彩图片

使用场景

全局概览呈现全局统计信息,重点查看整体是否有异常数据,所以有效信息有限,下面几种场景有一定帮助:

方法区溢出时(Java8后不使用方法区,对应堆溢出),查看class数量异常多,可以考虑是否为动态代理类异常载入过多或类被反复重复加载。

方法区溢出时,查看classloader数量过多,可以考虑是否为自定义classloader被异常循环使用。

GCRoot过多,可以查看GCRoot分布,理论上这种情况极少会遇到,笔者只在JNI使用一个存在BUG的库时遇到过。

线程数过多,一般是频繁创建线程但无法执行结束,从概览可以了解异常表象,具体原因可以参考本文线程分析部分内容,此处不展开。

2.2Dominatortree

注:笔者使用频率的Top1,是高效分析Dump必看的功能。

功能

展现对象的支配关系图,并给出对象支配内存的大小(支配内存等同于RetainedHeap,即其被GC回收可释放的内存大小)

支持排序、支持按package、classloader、superclass、class聚类统计

使用入口

:全局支配树:MAT主界面→Dominatortree。

举例:

下图中通过查看Dominatortree,了解到内存主要是由ThreadAndListHolder-thread及main两个线程支配(后面第2.6节会给出整体案例)。

打开网易新闻 查看精彩图片

使用场景

开始Dump分析时,首先应使用Dominatortree了解各支配树起点对象所支配内存的大小,进而了解哪几个起点对象是GC无法释放大内存的原因。

当个别对象支配树的RetainedHeap很大存在明显倾斜时,可以重点分析占比高的对象支配关系,展开子树进一步定位到问题根因,如下图中可看出最终是SameContentWrapperContainer对象持有的ArrayList过大。

打开网易新闻 查看精彩图片

在Dominatortree中展开树状图,可以查看支配关系路径(与outgoingreference的区别是:如果X支配Y,则X释放后Y必然可释放;如果仅仅是X引用Y,可能仍有其他对象引用Y,X释放后Y仍不能释放,所以Dominatortree去除了incomingreference中大量的冗余信息)。

有些情况下可能并没有支配起点对象的RetainedHeap占用很大内存(比如classX有100个对象,每个对象的RetainedHeap是10M,则classX所有对象实际支配的内存是1G,但可能Dominatortree的前20个都是其他class的对象),这时可以按class、package、classloader做聚合,进而定位目标。

下图中各GCRoots所支配的内存均不大,这时需要聚合定位爆发点。

打开网易新闻 查看精彩图片

在Dominatortree展现后按class聚合,如下图:

打开网易新闻 查看精彩图片

可以定位到是SomeEntry对象支配内存较多,然后结合代码进一步分析具体原因。

打开网易新闻 查看精彩图片

在一些操作后定位到异常持有RetainedHeap对象后(如从代码看对象应该被回收),可以获取对象的直接支配者,操作方式如下。

打开网易新闻 查看精彩图片
打开网易新闻 查看精彩图片

2.3Histogram直方图

注:笔者使用频率Top2

功能

罗列每个类实例的数量、类实例累计内存占比,包括自身内存占用量(ShallowHeap)及支配对象的内存占用量(RetainHeap)。

支持按对象数量、RetainedHeap、ShallowHeap(默认排序)等指标排序;支持按正则过滤;支持按package、classloader、superclass、class聚类统计,

使用入口

:MAT主界面→Histogram;注意Histogram默认不展现RetainedHeap,可以使用计算器图标计算,如下图所示。

打开网易新闻 查看精彩图片

使用场景

有些情况Dominatortree无法展现出热点对象(上文提到Dominatortree支配内存排名前20的占比均不高,或者按class聚合也无明显热点对象,此时Dominatortree很难做关联分析判断哪类对象占比高),这时可以使用Histogram查看所有对象所属类的分布,快速定位占据RetainedHeap大头的类。

使用技巧

Integer,String和Object[]一般不直接导致内存问题。为更好的组织视图,可以通过classloader或package分组进一步聚焦,如下图。

打开网易新闻 查看精彩图片

Histogram支持使用正则表达式来过滤。例如,我们可以只展示那些匹配com.q.*的类。

打开网易新闻 查看精彩图片

可以在Histogram的某个类继续使用outgoingreference查看对象分布,进而定位哪些对象是大头

打开网易新闻 查看精彩图片
打开网易新闻 查看精彩图片

2.4LeakSuspects

功能

:具备自动检测内存泄漏功能,罗列可能存在内存泄漏的问题点。

使用入口

:一般当存在明显的内存泄漏时,分析完Dump文件后就会展现,也可以如下图在MAT主页→LeakSuspects。

使用场景

:需要查看引用链条上占用内存较多的可疑对象。这个功能可解决一些基础问题,但复杂的问题往往帮助有限。

举例

下图中LeakSuspects视图展现了两个线程支配了绝大部分内存。

打开网易新闻 查看精彩图片

下图是点击上图中Keywords中"Details",获取实例到GCRoot的最短路径、dominator路径的细信息。

打开网易新闻 查看精彩图片

2.5TopConsumers

功能

:最大对象报告,可以展现哪些类、哪些classloader、哪些package占用最高比例的内存,其功能Histogram及Dominatortree也都支持。

使用场景

应用程序发生内存泄漏时,查看哪些泄漏的对象通常在Dump快照中会占很大的比重。因此,对简单的问题具有较高的价值。

2.6综合案例一

使用工具项

:Heapdumpoverview、Dominatortree、Histogram、ClassLoaderExplorer(见3.4节)、incomingreferences(见3.1节)

程序代码

packagecom.q.mat;importjava.util.*;importorg.objectweb.asm.*;publicclassClassLoaderOOMOpsext