31 JVM可视化诊断:MAT与JMC两款GUI工具实战指南

图片[1]-31 JVM可视化诊断:MAT与JMC两款GUI工具实战指南-速优课

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工具实战指南-速优课

MAT 获取堆快照的方式有三种:

  1. 直接使用 Attach API
  2. 启动一个新的 JVM 来运行 Attach API
  3. 调用 jmap 工具

本质上这三种方式都依赖 Attach API。如果目标进程开启了 -XX:+DisableAttachMechanism,前两种方式不会出现在选项列表中,第三种则会在运行时报错。

1.2 Shallow Heap 与 Retained Heap

加载完堆快照后,MAT 的主界面会展示一张饼图,列出占据 Retained heap 最多的几个对象。

图片[3]-31 JVM可视化诊断:MAT与JMC两款GUI工具实战指南-速优课

这里有两个关键概念需要理解:

概念定义
Shallow Heap对象自身占据的内存大小,也就是对象头 + 实例数据的大小
Retained Heap如果这个对象被回收,能释放的总内存大小,包括对象自身 + 仅通过该对象才能引用到的所有对象

简单来说,Shallow Heap 是”对象本身多大”,Retained Heap 是”对象带着它的’小弟们’一共占多大”。上面的饼图就是基于 Retained heap 来展示的。

1.3 直方图(Histogram)

MAT 有两个核心视图:直方图和支配树。

图片[4]-31 JVM可视化诊断:MAT与JMC两款GUI工具实战指南-速优课

直方图和 jmap -histo 的功能类似,展示每个类的实例数量以及 Shallow heap 总和。但 MAT 的直方图更强大:

  • 可以计算并展示 Retained heap
  • 支持按实例数量或 Retained heap 排序(默认按 Shallow heap)
  • 支持按超类、类加载器或包名分组

选中某个类后,左上角的 Inspector 窗口会展示该类的 Class 实例信息,比如类加载器等(下图中的 ClassLoader @ 0x0 代表启动类加载器)。

图片[5]-31 JVM可视化诊断:MAT与JMC两款GUI工具实战指南-速优课

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工具实战指南-速优课

根据 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工具实战指南-速优课

这个功能在定位内存泄漏时极其有用——它能清晰地展示出对象是被谁引用的,为什么还不能被回收。沿着引用链一路往上找,通常就能发现内存泄漏的根源。

此外,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 的 GetAllStackTraces API,而这个 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工具实战指南-速优课

在 JMC 左侧的 JVM 浏览器中,可以看到所有正在运行的 Java 进程。右键点击目标进程,选择 Start Flight Recording...,就会弹出配置窗口:

图片[9]-31 JVM可视化诊断:MAT与JMC两款GUI工具实战指南-速优课

这里的配置参数和前两种方式完全一致,包括标签名、采集时长、缓存限制、事件配置等。

JMC 提供了 Continuous 和 Profiling 两个预设选项,分别对应 default.jfcprofile.jfc 配置文件。

我们还可以通过 JMC 的 Flight Recording Template Manager 导入 jfc 配置文件,在 GUI 界面上自定义要采集的事件,修改完成后导出为新的 jfc 文件,供服务器端使用。

2.4 JFR 数据分析

采集完成后,JMC 会自动打开生成的 jfr 文件,主界面会列出这段时间内的潜在问题。比如 Parallel Threads 部分会报告 CPU 资源没有充分利用的问题。

图片[10]-31 JVM可视化诊断:MAT与JMC两款GUI工具实战指南-速优课

左侧列出了 JVM 的各个子系统,JMC 会根据采集到的事件数据生成各种图表和表格:

图片[11]-31 JVM可视化诊断:MAT与JMC两款GUI工具实战指南-速优课

我们简单介绍其中两个子系统:

垃圾回收(GC):

展示 GC 事件、堆已用空间分布图、Metaspace 大小分布图、GC 暂停时间直方图等。

图片[12]-31 JVM可视化诊断:MAT与JMC两款GUI工具实战指南-速优课

即时编译(Code):

展示方法编译时间直方图,以及按编译时间排序的编译任务列表。

你可能会在列表中看到同一个方法名出现多次,这通常有两个原因:

  1. 不同编译层次的编译(比如 C1 的 3 层编译和 C2 的 4 层编译)
  2. 去优化后的重新编译
图片[13]-31 JVM可视化诊断:MAT与JMC两款GUI工具实战指南-速优课

JMC 的各种图表都比较直观,这里就不一一展开了,建议大家自己动手探索一下。


总结与思考

本文要点回顾

  1. Eclipse MAT 是堆内存分析利器
    • Shallow Heap 是对象自身大小,Retained Heap 是对象及其依赖对象的总大小
    • 直方图展示各类的实例数量和内存占用
    • 支配树能快速定位”大对象”及其影响范围
    • Path to GC Roots 可以追踪对象的引用链,定位内存泄漏根源
  2. Java Flight Recorder 是生产级性能采集工具
    • 性能开销极低(默认 <1%),适合生产环境使用
    • 支持四种事件类型:瞬时事件、持续事件、计时事件、取样事件
    • 采样不依赖安全点,数据更准确
  3. JFR 有三种启用方式
    • 启动参数:适合预设的性能测试
    • jcmd 动态启用:适合线上应急排查
    • JMC 图形界面:适合开发和调试阶段
  4. JMC 提供了丰富的可视化分析
    • 自动检测潜在问题
    • GC、编译、线程、内存等各子系统的详细图表
    • 支持自定义事件配置模板

思考与实践

  1. 回想一下你遇到过的内存泄漏问题,如果用 MAT 来分析,你会怎么入手?
  2. JFR 的低开销特性让它可以在生产环境使用,你觉得可以用它来做什么?
  3. 对比命令行工具和 GUI 工具,它们各自的优势和适用场景是什么?

JVM 的监控诊断工具非常丰富,本文只介绍了最核心的两款。掌握好这些工具,能让你在排查性能问题时事半功倍。建议大家在本地环境多动手实践,熟悉它们的各种功能和用法。

© 版权声明
THE END
喜欢就支持一下吧
点赞15
相关推荐
评论 抢沙发

请登录后发表评论

    请登录后查看评论内容

温馨提示:
1、本内容转载于网络,版权归原作者所有!
2、本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
3、本内容若侵犯到你的版权利益,请联系我们,会尽快给予删除处理!