![图片[1]-31 JVM可视化诊断:MAT与JMC两款GUI工具实战指南-速优课](http://www.suyouke.com/wp-content/uploads/2026/07/doubao_img_2304x1728_20260722_174942-1-1024x768.png)
JVM可视化诊断:MAT与JMC两款GUI工具实战指南
本文导读
上一篇我们介绍了 JDK 自带的命令行诊断工具,它们在生产环境排查问题时非常高效。但有时候,我们需要更直观、更深入地分析问题,这时候图形化工具就能发挥巨大优势。
本文将介绍两款最常用的 JVM 可视化诊断工具:Eclipse MAT 和 Java Mission Control(JMC)。MAT 是分析堆内存、定位内存泄漏的利器,而 JMC 配合 Java Flight Recorder(JFR)则可以实现低开销的全链路性能分析。
通过阅读本文,你将了解:
- 什么是 Shallow heap 和 Retained heap
- 如何用 MAT 的直方图和支配树分析堆内存
- 如何通过 Path to GC Roots 定位内存泄漏
- Java Flight Recorder 的工作原理和事件类型
- JFR 的三种启用方式以及各自的适用场景
- 如何用 JMC 可视化分析 JFR 采集的数据
一、Eclipse MAT:堆内存分析神器
1.1 MAT 是什么
在上一篇文章中,我们介绍了用 jmap 导出 Java 堆的二进制快照(hprof 格式)。但拿到快照之后怎么分析呢?Eclipse MAT(Memory Analyzer Tool)就是最流行的堆快照分析工具之一。
MAT 本身也支持直接获取堆快照。它会先借助 jps 列出当前运行的 Java 进程,然后让你选择要分析的目标进程。
![图片[2]-31 JVM可视化诊断:MAT与JMC两款GUI工具实战指南-速优课](https://www.suyouke.com/wp-content/uploads/2026/07/image-29-1024x715.png)
MAT 获取堆快照的方式有三种:
- 直接使用 Attach API
- 启动一个新的 JVM 来运行 Attach API
- 调用
jmap工具
本质上这三种方式都依赖 Attach API。如果目标进程开启了 -XX:+DisableAttachMechanism,前两种方式不会出现在选项列表中,第三种则会在运行时报错。
1.2 Shallow Heap 与 Retained Heap
加载完堆快照后,MAT 的主界面会展示一张饼图,列出占据 Retained heap 最多的几个对象。
![图片[3]-31 JVM可视化诊断:MAT与JMC两款GUI工具实战指南-速优课](https://www.suyouke.com/wp-content/uploads/2026/07/image-30-1024x595.png)
这里有两个关键概念需要理解:
| 概念 | 定义 |
|---|---|
| Shallow Heap | 对象自身占据的内存大小,也就是对象头 + 实例数据的大小 |
| Retained Heap | 如果这个对象被回收,能释放的总内存大小,包括对象自身 + 仅通过该对象才能引用到的所有对象 |
简单来说,Shallow Heap 是”对象本身多大”,Retained Heap 是”对象带着它的’小弟们’一共占多大”。上面的饼图就是基于 Retained heap 来展示的。
1.3 直方图(Histogram)
MAT 有两个核心视图:直方图和支配树。
![图片[4]-31 JVM可视化诊断:MAT与JMC两款GUI工具实战指南-速优课](https://www.suyouke.com/wp-content/uploads/2026/07/image-31-1024x945.png)
直方图和 jmap -histo 的功能类似,展示每个类的实例数量以及 Shallow heap 总和。但 MAT 的直方图更强大:
- 可以计算并展示 Retained heap
- 支持按实例数量或 Retained heap 排序(默认按 Shallow heap)
- 支持按超类、类加载器或包名分组
选中某个类后,左上角的 Inspector 窗口会展示该类的 Class 实例信息,比如类加载器等(下图中的 ClassLoader @ 0x0 代表启动类加载器)。
![图片[5]-31 JVM可视化诊断:MAT与JMC两款GUI工具实战指南-速优课](https://www.suyouke.com/wp-content/uploads/2026/07/image-32-1024x428.png)
1.4 支配树(Dominator Tree)
支配树的概念源自图论。在一个流图中,如果从入口节点到 b 节点的所有路径都必须经过 a 节点,那么我们说 a 支配(dominate)b。
在此基础上,如果 a 严格支配 b(a ≠ b),且从 a 到 b 的路径中没有其他节点也支配 b,那么 a 就是 b 的直接支配节点(immediate dominator)。支配树就是由所有节点的直接支配节点组成的树状结构。
把这个概念应用到堆内存上:
- 每个对象是图的一个节点
- GC Roots 是对象图的入口
- 对象之间的引用关系是有向边
这样我们就能构造出堆对象图对应的支配树。MAT 会按每个对象的 Retained heap 大小对支配树进行排序:
![图片[6]-31 JVM可视化诊断:MAT与JMC两款GUI工具实战指南-速优课](https://www.suyouke.com/wp-content/uploads/2026/07/image-33-1024x557.png)
根据 Retained heap 的定义,只要能回收支配树中排在最前面的那个对象,GC 就能释放出 13.6MB 的内存。
注意:对象的引用字段和支配树的父子关系不是一一对应的。假设对象 a 有两个引用字段分别指向 b 和 c,而 b 和 c 各自有一个引用字段都指向 d。如果没有其他引用指向 b、c、d,那么 a 直接支配 b、c、d,而 b(或 c)和 d 之间没有支配关系。
1.5 Path to GC Roots
在支配树视图中选中某个对象后,我们还可以使用 Path To GC Roots 功能,反向列出从该对象到 GC Roots 的引用路径。
![图片[7]-31 JVM可视化诊断:MAT与JMC两款GUI工具实战指南-速优课](https://www.suyouke.com/wp-content/uploads/2026/07/image-34-1024x602.png)
这个功能在定位内存泄漏时极其有用——它能清晰地展示出对象是被谁引用的,为什么还不能被回收。沿着引用链一路往上找,通常就能发现内存泄漏的根源。
此外,MAT 还能自动匹配常见的内存泄漏模式,报告潜在的内存泄漏问题,帮你快速定位问题所在。
二、Java Mission Control:生产级性能监控
2.1 JMC 与 JFR 简介
Java Mission Control(JMC)是 Oracle 官方的性能监控工具,它包含一个 GUI 客户端和众多插件,其中最重要的就是 Java Flight Recorder(JFR)。
注意:从 Java 11 开始,JFR 已经开源。在更早的版本中,JFR 属于商业特性,需要通过
-XX:+UnlockCommercialFeatures开启。
JFR 是 JVM 内置的高性能事件采集框架,它有几个显著优势:
- 性能开销极低:默认配置下平均开销低于 1%
- 数据全面:可以采集 JVM 内部的各类事件
- 不干扰优化:直接访问虚拟机内部数据,不会影响 JIT 优化
正因为这些特点,JFR 非常适合在生产环境中对满负荷运行的 Java 应用进行性能分析。
2.2 JFR 的事件类型
启用 JFR 后,它会记录运行过程中发生的一系列事件,包括:
- Java 层面的事件:线程事件、锁事件、异常事件等
- JVM 内部的事件:对象分配、垃圾回收、即时编译等
根据发生时机和持续时间,JFR 的事件分为四类:
| 事件类型 | 说明 | 例子 |
|---|---|---|
| 瞬时事件(Instant Event) | 只关心是否发生,不关心持续时间 | 异常抛出、线程启动 |
| 持续事件(Duration Event) | 关心事件的持续时间 | GC 事件、方法调用 |
| 计时事件(Timed Event) | 只记录时长超过阈值的持续事件 | 超过阈值的 SQL 查询 |
| 取样事件(Sample Event) | 周期性采样的事件 | 方法抽样、内存抽样 |
方法抽样(Method Sampling)是最常见的取样事件之一:每隔一段时间统计一次各个线程的栈轨迹,如果某个方法在采样中反复出现,说明它大概率是热点方法。
JFR 的取样事件比其他工具更精确。原因在于:
- 其他工具通常基于 JVMTI 的
GetAllStackTracesAPI,而这个 API 依赖安全点机制,获取的栈轨迹总是在安全点位置,可能产生偏差 - JFR 不依赖安全点机制,采样结果更加真实可靠
2.3 JFR 的三种启用方式
JFR 有三种启用方式,分别适用于不同场景。
方式一:启动参数启用
第一种方式是在启动 Java 程序时通过 -XX:StartFlightRecording= 参数启用 JFR。
下面是三种常见的配置方式:
固定时长采集:
# 启动 5 秒后开始采集,持续 20 秒,采集完成后保存到文件
$ java -XX:StartFlightRecording=delay=5s,duration=20s,filename=myrecording.jfr,settings=profile MyApp
参数说明:
delay=5s:延迟 5 秒后开始采集duration=20s:采集持续 20 秒filename=myrecording.jfr:采集完成后保存到指定文件settings=profile:使用 profile 配置文件
关于 settings 参数多说两句:
- 默认使用
$JDK/lib/jfr/default.jfc配置文件,开销较低(<1%) - 使用
settings=profile会加载$JDK/lib/jfr/profile.jfc,采集的事件更多更详细,开销约为 2% - 这两个文件都是 XML 格式,后面会介绍如何用 JMC 自定义配置
持续采集 + 退出时 dump:
# 持续采集,进程退出时自动保存到文件
$ java -XX:StartFlightRecording=dumponexit=true,filename=myrecording.jfr MyApp
持续采集 + 按需 dump:
# 持续采集,限制最多保留 10 分钟 / 100MB 的数据
$ java -XX:StartFlightRecording=maxage=10m,maxsize=100m,name=SomeLabel MyApp
Started recording 1.
Use jcmd 38502 JFR.dump name=SomeLabel filename=FILEPATH to copy recording data to file.
...
这种模式下 JFR 会持续采集数据,但需要设置限制来避免磁盘被占满:
maxage=10m:只保留最近 10 分钟的事件maxsize=100m:最多保留 100MB 的事件name=SomeLabel:给这个采集任务起个名字,方便后续操作
注意:为了保持低开销,JFR 不会频繁校验这两个限制。实际使用中你可能会发现文件大小或事件时间略微超出限制,这是正常现象。
方式二:jcmd 动态启用
第二种方式是通过 jcmd 命令在进程运行过程中动态控制 JFR。这对应三个子命令:
JFR.start:开始采集JFR.dump:导出已采集的数据JFR.stop:停止采集
启动目标进程时不需要加任何 JFR 相关参数。当需要分析时,执行以下命令:
# 启动 JFR 采集
$ jcmd <PID> JFR.start settings=profile maxage=10m maxsize=150m name=SomeLabel
采集一段时间后,导出数据:
# 导出当前已采集的数据
$ jcmd <PID> JFR.dump name=SomeLabel filename=myrecording.jfr
最后可以选择关闭 JFR:
# 停止 JFR 采集
$ jcmd <PID> JFR.stop name=SomeLabel
这种方式非常适合线上应急排查——平时不开启,需要时再启动,对性能零影响。
方式三:JMC 图形界面启用
第三种方式是通过 JMC 的 GUI 来操作。
![图片[8]-31 JVM可视化诊断:MAT与JMC两款GUI工具实战指南-速优课](https://www.suyouke.com/wp-content/uploads/2026/07/image-35.png)
在 JMC 左侧的 JVM 浏览器中,可以看到所有正在运行的 Java 进程。右键点击目标进程,选择 Start Flight Recording...,就会弹出配置窗口:
![图片[9]-31 JVM可视化诊断:MAT与JMC两款GUI工具实战指南-速优课](https://www.suyouke.com/wp-content/uploads/2026/07/image-36-1024x785.png)
这里的配置参数和前两种方式完全一致,包括标签名、采集时长、缓存限制、事件配置等。
JMC 提供了 Continuous 和 Profiling 两个预设选项,分别对应
default.jfc和profile.jfc配置文件。
我们还可以通过 JMC 的 Flight Recording Template Manager 导入 jfc 配置文件,在 GUI 界面上自定义要采集的事件,修改完成后导出为新的 jfc 文件,供服务器端使用。
2.4 JFR 数据分析
采集完成后,JMC 会自动打开生成的 jfr 文件,主界面会列出这段时间内的潜在问题。比如 Parallel Threads 部分会报告 CPU 资源没有充分利用的问题。
![图片[10]-31 JVM可视化诊断:MAT与JMC两款GUI工具实战指南-速优课](https://www.suyouke.com/wp-content/uploads/2026/07/image-37-1024x566.png)
左侧列出了 JVM 的各个子系统,JMC 会根据采集到的事件数据生成各种图表和表格:
![图片[11]-31 JVM可视化诊断:MAT与JMC两款GUI工具实战指南-速优课](https://www.suyouke.com/wp-content/uploads/2026/07/image-38-589x1024.png)
我们简单介绍其中两个子系统:
垃圾回收(GC):
展示 GC 事件、堆已用空间分布图、Metaspace 大小分布图、GC 暂停时间直方图等。
![图片[12]-31 JVM可视化诊断:MAT与JMC两款GUI工具实战指南-速优课](https://www.suyouke.com/wp-content/uploads/2026/07/image-39-1024x573.png)
即时编译(Code):
展示方法编译时间直方图,以及按编译时间排序的编译任务列表。
你可能会在列表中看到同一个方法名出现多次,这通常有两个原因:
- 不同编译层次的编译(比如 C1 的 3 层编译和 C2 的 4 层编译)
- 去优化后的重新编译
![图片[13]-31 JVM可视化诊断:MAT与JMC两款GUI工具实战指南-速优课](https://www.suyouke.com/wp-content/uploads/2026/07/image-40-1024x571.png)
JMC 的各种图表都比较直观,这里就不一一展开了,建议大家自己动手探索一下。
总结与思考
本文要点回顾
- Eclipse MAT 是堆内存分析利器:
- Shallow Heap 是对象自身大小,Retained Heap 是对象及其依赖对象的总大小
- 直方图展示各类的实例数量和内存占用
- 支配树能快速定位”大对象”及其影响范围
- Path to GC Roots 可以追踪对象的引用链,定位内存泄漏根源
- Java Flight Recorder 是生产级性能采集工具:
- 性能开销极低(默认 <1%),适合生产环境使用
- 支持四种事件类型:瞬时事件、持续事件、计时事件、取样事件
- 采样不依赖安全点,数据更准确
- JFR 有三种启用方式:
- 启动参数:适合预设的性能测试
- jcmd 动态启用:适合线上应急排查
- JMC 图形界面:适合开发和调试阶段
- JMC 提供了丰富的可视化分析:
- 自动检测潜在问题
- GC、编译、线程、内存等各子系统的详细图表
- 支持自定义事件配置模板
思考与实践
- 回想一下你遇到过的内存泄漏问题,如果用 MAT 来分析,你会怎么入手?
- JFR 的低开销特性让它可以在生产环境使用,你觉得可以用它来做什么?
- 对比命令行工具和 GUI 工具,它们各自的优势和适用场景是什么?
JVM 的监控诊断工具非常丰富,本文只介绍了最核心的两款。掌握好这些工具,能让你在排查性能问题时事半功倍。建议大家在本地环境多动手实践,熟悉它们的各种功能和用法。








请登录后查看评论内容