【JVM】分析Dump日志的工具EclipseMAT
- 【一】初始配置
- 【1】环境配置
- 【2】基础概念
- 【二】各个功能介绍
- 【1】Overview(总览面板)
- (1)Details 概览区
- (2)Actions 快捷工具(高频使用)
- (3)Reports 报告区
- (4)Inspector 对象详情面板
- 【2】Histogram(直方图)
- (1)打开方式
- (2)功能描述
- (3)案例
- (4)菜单关键选项
- 【3】Dominator Tree(支配树)定位内存根源持有者
- (1)打开方式
- (2)功能描述
- (3)案例
- (4)右键功能
- 【4】Group分组功能
- 【5】Thread Overview
- 【6】List objects
- (1)with outgoing references
- (2)with incoming references
- 【7】Path to GC Roots && Merge Shortets Paths to GC roots
- (1)Paths to GC Roots
- (2)Merge Shortest Paths to GC roots
- (3)菜单选项
- 【8】Leak Suspects Report 泄漏嫌疑自动报告(新手一键分析)
- 【9】Class Loader Explorer 类加载器专属视图
- (1)打开方式
- (2)功能描述
- 【10】GC Roots 根路径分析(判断对象是否满足 GC 条件)
- 【三】使用案例
- 【1】Leak Suspects:泄漏嫌疑报告(最常用)
- 【四】# 关键区分易混淆选项
【一】初始配置
【1】环境配置
(1)要求JDK version >=17
(2)heap.hprof文件往往比较大,需要修改MemoryAnalyer.ini文件中设置
-vmargs-Xmx26g-Xms4g-XX:+UseG1GC-XX:+UseCompressedOops-XX:MaxGCPauseMillis=500-XX:+DisableExplicitGC-Dsun.rmi.dgc.client.gcInterval=3600000【2】基础概念
- Shallow Heap(浅堆)
对象本身占用内存,不包含引用的其他对象;比如DelegatingClassLoader自身对象几十字节。 - Retained Heap(保留堆)
该对象一旦被 GC 回收,能连带释放的全部内存总和;判断内存泄漏最核心指标。 - Histogram(直方图):按类统计实例数、浅堆、保留堆,快速定位数量爆炸类(如大量
DelegatingClassLoader)。 - Dominator Tree(支配树):找出占用内存最大的 “罪魁对象”,支配对象存活则所有下级对象都无法 GC。
- Leak Suspects Report(泄漏嫌疑报告):MAT 自动分析生成的一键诊断报告,新手首选。
- Incoming References(入引用):谁持有当前对象的强引用(你之前 DCL 泄漏最关键视图,找阻断 GC 的引用链)。
- Outgoing References(出引用):当前对象持有哪些其他对象。
【二】各个功能介绍
【1】Overview(总览面板)
页面分为 4 大区块,快速掌握整体内存状态:
(1)Details 概览区
- Heap Size:dump 时总堆内存、已使用内存;
- Number of Objects:堆内全部对象总数;
- Class Loaders:统计所有类加载器数量(直接看有多少 DelegatingClassLoader、RestartClassLoader);
- Unreachable Objects Histogram:已经没有强引用、等待 GC 回收的对象,数量大说明内存碎片多。
(2)Actions 快捷工具(高频使用)
表格
| 快捷按钮 | 对应功能 | 适用场景(你的 DCL 泄漏场景) |
|---|---|---|
| Histogram | 直方图视图 | 统计DelegatingClassLoader总实例数、GeneratedMethodAccessor数量 |
| Dominator Tree | 支配树 | 找到 MybatisMybatisConfiguration这个大对象,它支配所有反射 Method、DCL |
| Top Consumers | 内存消耗 TOP 对象 | 查看占用内存最高的单例 Bean(SqlSessionFactory、ReflectorFactory) |
| Duplicate Classes | 重复类检测 | 热部署场景RestartClassLoader重复加载同一 PO 类,成倍产生 DCL |
(3)Reports 报告区
- Leak Suspects:自动识别内存泄漏点,给出完整引用链;泄漏嫌疑报告(最常用);
- Component Report:按 ClassLoader 分组统计,单独看 AppClassLoader/DelegatingClassLoader 内存占用;
- Top Components Report:超大组件内存分析(Mybatis 配置、Spring 容器);
(4)Inspector 对象详情面板
选中任意对象,底部自动展示:
- Class Name:类全限定名;
- Shallow/Retained 堆大小;
- 字段列表:该对象所有成员变量(定位
reflectorFactory、configuration引用)。
通过 File > Open Heap Dump… 打开dump文件,最先展示就是Overview概述界面,可以对Heap Dump有一个大致的了解,并提供了一些视图、报告的入口,这些视图、报告都对分析Heap Dump很有帮助,后续会介绍。
鼠标移动到饼图某个区域上方,在左侧会看到对象的详细信息,如左上方的 Inspector 展示了如:对象hashcode、类名、包名、Class类对象、父类、类加载器、shallow size、retained size、GC root类型。左下方展示了对象的一些属性信息、类层级信息。
【2】Histogram(直方图)
(1)打开方式
以下方式可以打开Histogram柱状图:
(1)点击Overview页面Actions区域内的“Histogram视图”链接
(2)点击工具栏的“histogram按钮”
(2)功能描述
列含义
- Class Name:类名,支持正则搜索
DelegatingClassLoader、GeneratedMethodAccessor; - Objects:该类实例总数(你最关心的 DCL 数量在这里直接看);
- Shallow Heap:所有实例自身占用总内存;
- Retained Heap:回收该类所有实例可释放总内存。
不过,多数情况下,在Histogram视图看到实例对象数量比较多的类都是一些基础类型,如char[](因为其构成了String)、String、byte[],所以仅从这些是无法判断出具体导致内存泄露的类或者方法的,可以使用 List objects 或 Merge Shortest Paths to GC roots 等功能继续钻取数据。如果Histogram视图展示的数量多的实例对象不是基础类型,是有嫌疑的某个类,如项目代码中的bean类型,那么就要重点关注了。
(3)案例
(4)菜单关键选项
- List objects → with incoming references
【重中之重】显示所有指向当前对象的强引用链,就是你截图里的完整链路:GeneratedMethodAccessor → DelegatingMethodAccessorImpl → Method → MethodInvoker → Reflector → DefaultReflectorFactory → MybatisConfiguration
用来定位谁锁住 DCL 导致无法 GC,元空间泄漏核心操作。 - List objects → with outgoing references
查看当前对象持有哪些下级对象,比如查看 Reflector 缓存了多少 Method。 - Show as Histogram / Show as Pie Chart
饼图可视化内存占比,直观看到 Mybatis 缓存占用大头。 - Filter:过滤实例数、内存阈值,过滤掉小对象,只看爆炸增长的类。
- Merge Shortest Paths to GC Roots
一键提取最短 GC 根引用链,直接找到 Spring 单例、静态变量、线程等根对象,跳过冗长中间链路。 - Group by → Class Loader
按类加载器分组统计,区分:AppClassLoader、DelegatingClassLoader、热部署 RestartClassLoader,清晰看到每个加载器分别创建了多少对象。
【3】Dominator Tree(支配树)定位内存根源持有者
支配对象:如果 A 对象存活,B 对象永远无法被 GC,称 A 支配 B。
(1)打开方式
以下方式可以打开Dominator Tree视图:
(1)点击Overview页面Actions区域内的“Dominator Tree视图”链接
(2)点击工具栏的“Dominator Tree按钮” ,为整个堆打开一个支配树视图
(2)功能描述
列含义
- Class Name:对象类型;
- Retained Heap:该对象支配的总内存;
- Percentage:占堆总内存比例。
该视图以实例对象的维度展示当前堆内存中Retained Heap占用最大的对象,以及依赖这些对象存活的对象的树状结构。视图中展示了实例对象名、Shallow Heap大小、Retained Heap大小、以及当前对象的Retained Heap在整个堆中的占比。
点开Dominator Tree实例对象左侧的“+”,会展示出下一层(next level),当所有引用了当前实例对象的引用都被清除后,下一层列出的objects就会被垃圾回收
这也阐明了“支配”的含义:父节点的回收会导致子节点也被回收,即因为父节点的存在使得子节点存活
Dominator Tree支配树可以很方便的找出占用Retained Heap内存最多的几个对象,并表示出某些objects的是因为哪些objects的原因而存活,在之后的 Dominator Tree概念 部分会对支配树做更详细的说明和举例
(3)案例
直接找到根支配对象
(4)右键功能
- Merge Shortest Paths to GC Roots:直达 GC 根(Spring 容器);
- Show Retained Set:展开所有被该对象锁住的下级对象,批量查看所有 DCL 实例。
【4】Group分组功能
使用Group分组功能的方法是,在 Histogram视图 和 Domiantor Tree视图时,点击工具栏的 Group result by…
可以选择以另一种分组方式显示(默认是No Grouping(objects),即以对象维度分组)
例如在Histogram视图 或 Dominator Tree视图,选择Group by package,可以更好地查看具体是哪个包里的类占用内存大,也很容易定位到自己的应用程序
【5】Thread Overview
Thread视图的入口,在工具栏上:
Thread Overview:
在Thread Overview视图可以看到:线程对象/线程栈信息、线程名、Shallow Heap、Retained Heap、类加载器、是否Daemon线程等信息
在分析内存Dump的MAT中还可以看到线程栈信息,这本身就是一个强大的功能,类似于jstack命令的效果
而且还能结合内存Dump分析,看到线程栈帧中的本地变量,在左下方的对象属性区域还能看到本地变量的属性,真的很方便
publicclassTestThreadOverview{privateStringstr1="str1";privateStringstr2="str2";publicstaticvoidmain(String[]args){TestThreadOverviewtest=newTestThreadOverview();Stringlocal_str="local_str";LockSupport.park();}}在上面代码的Heap Dump分析中,可以看到线程调用栈的信息,以及main线程的 本地变量TestThreadOverview 和 字符串local_str 的信息
上图中第一个框起来的部分是 new TestThreadOverview()对象(代码第6行),TestThreadOverview对象有两个属性str1、str2
第二个框起来的部分是main方法中的字符串变量local_str(代码第8行)
结合左侧的对象属性区域,可以更方便的看清线程中对象的具体情况
【6】List objects
在 Histogram 或 Dominator Tree视图,想要看某个条目(对象/类)的引用关系图,可以使用 List objects 功能
(1)选择一个条目后,点击工具栏的 Query Browser > List objects,选择 with outgoing references 或 with incoming references
(2)直接在某个条目上点击右键,也可以选择到List object
(1)with outgoing references
查看当前对象持有的外部对象引用(在对象关系图中为从当前对象指向外的箭头)
例如上面Thread Overview的例子代码中,查看main方法中第6行中的:TestThreadOverview test = new TestThreadOverview();
outgoing references查询结果为:
可以看到TestThreadOverview对象存在3个引用,第一个是TestThreadOverview的Class类对象,因为所有Java类都继承自java.lang.Object,所以都有class对象的引用,后两个是成员变量str1、str2
即列出了当前main方法中的局部变量TestThreadOverview所持有的所有外部对象引用
(2)with incoming references
查看当前对象被哪些外部对象所引用(在对象关系图中为指向当前对象的箭头)
例如上面Thread Overview的例子代码中,查看main方法中第6行中的:TestThreadOverview test = new TestThreadOverview();
incoming references查询结果为:
可以看到TestThreadOverview是main线程的一个本地局部变量,main线程本身还是一个GC root,而main线程在某个ThreadGroup中
【7】Path to GC Roots && Merge Shortets Paths to GC roots
Path to GC roots 和 Merge shortest Paths to GC roots 的入口和 List objects一样,可以从工具栏的 Query Browser 进入,或者在条目上直接点击右键进入
需要注意的是,Paths to GC roots是针对单个对象的,故在Histogram视图无法使用,因为Histogram视图是针对类的,只能使用Merge shortest Paths to GC roots查询
(1)Paths to GC Roots
从当前对象到GC roots的路径,这个路径解释了为什么当前对象还能存活,对分析内存泄露很有帮助,这个查询只能针对单个对象使用
(2)Merge Shortest Paths to GC roots
从GC roots到一个或一组对象的公共路径
(3)菜单选项
Path to GC roots 和 Merge shortest Paths to GC roots 这两个查询都有很多选项,如:
意思是在查询到GC root的路径时,是包含所有引用,还是排除一些类型的引用(如软引用、弱引用、虚引用),从GC角度说,一个对象无法被GC,一定是因为有强引用存在,其它引用类型在GC需要的情况下都是可以被GC掉的,所以可以使用 exclude all phantom/weak/soft etc. references 只查看GC路径上的强引用
【8】Leak Suspects Report 泄漏嫌疑自动报告(新手一键分析)
使用MAT打开一个Dump文件时,会弹出向导窗口,保持默认选项,点Finish,就会导向 Leak Suspects内存泄露报告页面,MAT 自动扫描 dump,把高风险泄漏点分优先级展示,分为 3 部分:
- Suspect Summary 泄漏摘要
列出 TOP3 嫌疑泄漏点,描述问题、预估可释放内存;你的 dump 会标记:大量GeneratedMethodAccessor被 Mybatis 反射缓存持有,导致元空间 ClassLoader 堆积。 - Suspect Details 详情
自动生成精简引用链,不用手动逐层展开; - Comparison Report
支持对比两个不同时间的 dump,观察哪些类实例持续增长(对比前后 dump,DCL 数量持续上涨直接实锤泄漏)。
功能:加载.hprof堆快照文件,加载后弹出选择界面:
- Leak Suspects Report(默认推荐):自动生成泄漏分析报告,适合快速排查元空间泄漏、OOM、堆溢出;
- Component Report:按业务组件、类加载器分组分析内存;
- 取消勾选:仅打开空白工作台,手动打开 Histogram/Dominator Tree。
如果打开Dump时跳过了的话,也可以从其它入口进入,如
(1)工具栏上的 Run Expect System Test > Leak Suspects
(2)Overview页面的Reports部分
Leak Suspects 是MAT帮我们分析的可能有内存泄露嫌疑的地方,可以体现出哪些对象被保持在内存中,以及为什么它们没有被垃圾回收
MAT提供了一个很贴心的功能,将报告的内容压缩打包到一个zip文件,并放在原始堆转储文件的目录下,一般命名为“xxx_Leak_Suspects.zip”,xxx是dump文件的名字,如果需要和同事一起分析这个内存问题的话,只需要把这个小小的zip包发给他就可以了,不需要把整个堆文件发给他。并且整个报告是一个HTML格式的文件,用浏览器就可以轻松打开
【9】Class Loader Explorer 类加载器专属视图
(1)打开方式
(2)功能描述
按 ClassLoader 树形展示所有加载的类、实例
- 树形分层展示:BootClassLoader → ExtClassLoader → AppClassLoader → DelegatingClassLoader;
- 统计每个 ClassLoader 加载了多少类、对象、占用元空间;
- 右键单个 DelegatingClassLoader:
- List all Classes:查看该加载器加载的所有
GeneratedMethodAccessor动态类; - Show GC Roots:查看这个 DCL 被谁强引用,判断能否回收。
- List all Classes:查看该加载器加载的所有
单独筛选所有DelegatingClassLoader实例,统计总数,区分业务 ClassLoader 和反射专属加载器,直观看到 Mybatis 缓存创建了几千个 DCL。
【10】GC Roots 根路径分析(判断对象是否满足 GC 条件)
打开方式:任意对象右键 →Merge Shortest Paths to GC Roots
(1)GC Roots 类型(阻断 DCL 回收的常见根)
- Static Fields:静态全局缓存(Mybatis DefaultReflectorFactory 是静态单例);
- Thread:线程池核心线程、定时任务线程持有上下文 ClassLoader;
- System Class:JDK 系统类(ReflectionFactory 全局缓存);
- Local Variable:方法内未释放的局部变量(长期存活线程的 ThreadLocal)。
(2)判断标准(对应你之前 DCL GC 三条件)
- 若 GC Roots 路径存在业务静态 / 单例 Bean→ DCL 存在永久强引用,不满足 GC 条件;
- 若仅存在
ReflectionFactoryWeakHashMap 弱引用,无其他 GC 根 → 满足 GC,Full GC 后可回收。
【三】使用案例
【1】Leak Suspects:泄漏嫌疑报告(最常用)
【四】# 关键区分易混淆选项
with incoming referencesvswith outgoing references- incoming:谁引用我(找泄漏根源,必用);
- outgoing:我引用谁(看缓存存了多少下级对象)。
- Shallow Heap vs Retained Heap
- Shallow:对象本身大小,无参考价值;
- Retained:回收该对象能释放的总内存,判断泄漏严重程度。
- Histogram vs Dominator Tree
- Histogram:按类统计实例数量(查 DCL 总数);
- Dominator Tree:按单个对象统计支配内存(找根单例 Bean)。
- Merge Shortest Paths to GC Roots
过滤多层中间无用对象,直接展示从 GC 根到目标 DCL 的最短链路,简化分析。