1. 前言
熟练掌握 MAT 是 Java 高手的必备能力,但实践时大家往往需面对众多功能,眼花缭乱不知如何下手,小编也没有找到一篇完善的教学素材,所以整理本文帮大家系统掌握 MAT 分析工具。
本文详细讲解 MAT 众多内存分析工具功能,这些功能组合使用异常强大,熟练使用几乎可以解决所有的堆内存离线分析的问题。我们将功能划分为4类:内存分布详情、对象间依赖、对象状态详情、按条件检索。每大类有多个功能点,本文会逐一讲解各功能的场景及用法。此外,添加了原创或引用案例加强理解和掌握。
如图所示:
为减少对眼花缭乱的菜单的迷茫,可以通过下图先整体熟悉下各功能使用入口,后续都会讲到。
2. 内存分布详解及实战
2.1 全局信息概览
功能:展现堆内存大小、对象数量、class 数量、class loader 数量、GC Root 数量、环境变量、线程概况等全局统计信息。
使用入口:MAT 主界面 → Heap Dump Overview。
举例:下面是对象数量、class loader 数量、GC Root 数量,可以看出 class loader 存在异常。
举例:下图是线程概况,可以查看每个线程名、线程的 Retained Heap、daemon 属性等。
使用场景 全局概览呈现全局统计信息,重点查看整体是否有异常数据,所以有效信息有限,下面几种场景有一定帮助:
- 方法区溢出时(Java 8后不使用方法区,对应堆溢出),查看 class 数量异常多,可以考虑是否为动态代理类异常载入过多或类被反复重复加载。
- 方法区溢出时,查看 class loader 数量过多,可以考虑是否为自定义 class loader 被异常循环使用。
- GC Root 过多,可以查看 GC Root 分布,理论上这种情况极少会遇到,笔者只在 JNI 使用一个存在 BUG 的库时遇到过。
- 线程数过多,一般是频繁创建线程但无法执行结束,从概览可以了解异常表象,具体原因可以参考本文线程分析部分内容,此处不展开。
2.2 Dominator tree
注:笔者使用频率的 Top1,是高效分析 Dump 必看的功能。
功能
- 展现对象的支配关系图,并给出对象支配内存的大小(支配内存等同于 Retained Heap,即其被 GC 回收可释放的内存大小)
- 支持排序、支持按 package、class loader、super class、class 聚类统计
使用入口:全局支配树: MAT 主界面 → Dominator tree。
举例: 下图中通过查看 Dominator tree,了解到内存主要是由
ThreadAndListHolder-thread 及 main 两个线程支配(后面第2.6节会给出整体案例)。
使用场景
- 开始 Dump 分析时,首先应使用 Dominator tree 了解各支配树起点对象所支配内存的大小,进而了解哪几个起点对象是 GC 无法释放大内存的原因。
- 当个别对象支配树的 Retained Heap 很大存在明显倾斜时,可以重点分析占比高的对象支配关系,展开子树进一步定位到问题根因,如下图中可看出最终是 SameContentWrapperContainer 对象持有的 ArrayList 过大。
- 在 Dominator tree 中展开树状图,可以查看支配关系路径(与 outgoing reference 的区别是:如果 X 支配 Y,则 X 释放后 Y必然可释放;如果仅仅是 X 引用 Y,可能仍有其他对象引用 Y,X 释放后 Y 仍不能释放,所以 Dominator tree 去除了 incoming reference 中大量的冗余信息)。
- 有些情况下可能并没有支配起点对象的 Retained Heap 占用很大内存(比如 class X 有100个对象,每个对象的 Retained Heap 是10M,则 class X 所有对象实际支配的内存是 1G,但可能 Dominator tree 的前20个都是其他class 的对象),这时可以按 class、package、class loader 做聚合,进而定位目标。
- 下图中各 GC Roots 所支配的内存均不大,这时需要聚合定位爆发点。
在 Dominator tree 展现后按 class 聚合,如下图:
可以定位到是 SomeEntry 对象支配内存较多,然后结合代码进一步分析具体原因。
在一些操作后定位到异常持有 Retained Heap 对象后(如从代码看对象应该被回收),可以获取对象的直接支配者,操作方式如下。
2.3 Histogram 直方图
注:笔者使用频率 Top2
功能
- 罗列每个类实例的数量、类实例累计内存占比,包括自身内存占用量(Shallow Heap)及支配对象的内存占用量(Retain Heap)。
- 支持按对象数量、Retained Heap、Shallow Heap(默认排序)等指标排序;支持按正则过滤;支持按 package、class loader、super class、class 聚类统计,
使用入口:MAT 主界面 → Histogram;注意 Histogram 默认不展现 Retained Heap,可以使用计算器图标计算,如下图所示。
使用场景
- 有些情况 Dominator tree 无法展现出热点对象(上文提到 Dominator tree 支配内存排名前20的占比均不高,或者按 class 聚合也无明显热点对象,此时 Dominator tree 很难做关联分析判断哪类对象占比高),这时可以使用 Histogram 查看所有对象所属类的分布,快速定位占据 Retained Heap 大头的类。
使用技巧
- Integer,String 和 Object[] 一般不直接导致内存问题。为更好的组织视图,可以通过 class loader 或 package 分组进一步聚焦,如下图。
Histogram 支持使用正则表达式来过滤。例如,我们可以只展示那些匹配com.q.*的类。