JVM指令与工具使用
2026/9/7 11:03:16 网站建设 项目流程

文章目录

    • 1. JVM默认参数
    • 2. jvm工具
      • 2.1 Jps
      • 2.2 jconsole
      • 2.3 jstat
      • 2.4 jstack线程
      • 2.5 jmap工具
      • 2.6 visualvm工具
      • 2.7 jcmd工具
    • 3. OutOfMemory(内存溢出)演示
      • 3.1 堆溢出
        • 3.1.1 回收效率不足2%,抛出OutOfMemoryError: GC overhead limit exceeded
        • 3.1.2 单次分配对象过大,堆无法容纳,抛出OutOfMemoryError: Java heap space
      • 3.2 栈溢出
        • 3.2.1 死递归,抛出StackOverflowError
        • 3.2.2 线程过多,抛出OutOfMemoryError
    • 注意:
      • 3.3 直接内存溢出,抛出Direct buffer memory
      • 3.4 方法区溢出java.lang.OutOfMemoryError: Metaspace
    • 4. GC日志查看
    • 5.在线诊断工具Arthas
      • 5.1 下载
      • 5.2 命令
        • 5.2.1 dashboard
        • 5.2.2 thread
        • 5.2.3 jvm
        • 5.2.4 trace
        • 5.2.5 sc
        • 5.2.6 jad
        • 5.2.7 watch
        • 5.2.8 getstatic
        • 5.2.9 OGNL
      • 5.3 火焰图
    • 6.离线内存分析Eclipse MAT
      • 6.0 下载安装
      • 6.1 MAT的作用
      • 6.2 MAT的使用流程
      • 6.3 导出Heap Dump
      • 6.4 打开Heap Dump
      • 6.5 MAT中最常用的几个视图
        • 6.5.1 Histogram
        • 6.5.2 Dominator Tree
        • 6.5.3 Path to GC Roots
      • 6.6 使用案例:分析集合对象导致的内存占用
      • 6.7 案例分析思路
        • 第一步:查看 Leak Suspects Report(打开时勾选了就会直接打开,右侧的default_report就是)
        • 第二步:查看 Histogram
        • 第三步:查看 Dominator Tree
        • 第四步:查看 Path to GC Roots
          • Path to GC Roots 与 Merge Shortest Paths to GC Roots 的区别
      • 6.8 结合案例总结MAT排查步骤
      • 6.9 MAT与Arthas的区别
      • 6.10 小结

1. JVM默认参数

#查看JVM参数java-XX:+PrintCommandLineFlags-version

cmd中查看

-XX:InitialHeapSize=263893632,表示初始堆内存263893632字节,250M左右。
-XX:MaxHeapSize=4222298112,最大堆内存约4G。
本机内存16G,本例 JDK8 HotSpot 使用 Parallel GC,以上结果正好符合默认最小1/64及最大1/4。不同 JDK、收集器及容器环境可能有所不同。

也可以在IDEA中查看
在VM options虚拟机参数中添加-XX:+PrintCommandLineFlags

启动程序


将初始及最大内存设置为10M后两次运行

2. jvm工具

2.1 Jps

jps查看java相关Pid

2.2 jconsole


点击主程序Tuning进入
概览

内存使用情况,可以查看堆区与非堆区的内存变化,并且详细到新生代和老年代,收集次数就是GC次数

可以手动发起显式 GC 请求,本质上等价于调用 System.gc();最终以何种方式执行由垃圾收集器和 JVM 配置决定,不是必然 Full GC。
还可以查看线程情况等,此处不赘述

2.3 jstat

#jstat -gcutil pid 间隔时间jstat-gcutil412361000


也可以打印总信息,也可以只查看新生代或老年代信息

2.4 jstack线程

2.5 jmap工具

jmap工具可以下载或直接打印jvm信息

# 下载jvm信息# jmap -dump:file=文件名 pidjmap-dump:file=info41236# 打印信息# jmap -heap pidjmap-heap41236

现代 JDK 查看堆信息更推荐使用 jcmd。” Oracle 当前也把 jcmd 作为重要诊断接口,并支持 GC.heap_info、GC.heap_dump 等命令。

2.6 visualvm工具

JDK8 中 jvisualvm 随 JDK 提供;JDK9 以后 VisualVM 已从 JDK 中移除,需要单独下载安装。





2.7 jcmd工具


常用命令:

# 查看支持的诊断命令jcmd<pid>help# 查看堆信息jcmd<pid>GC.heap_info# 查看线程栈jcmd<pid>Thread.print# 查看对象直方图jcmd<pid>GC.class_histogram# 导出堆快照jcmd<pid>GC.heap_dumpfilename=heap.hprof

3. OutOfMemory(内存溢出)演示

当出现堆溢出时,要先分清楚到底是出现了内存泄漏(Memory Leak)还是内存溢出(Memory Overflow)。

内存泄漏是指对象在业务上已经不再需要,但由于仍被某条强引用链持有,从 GC Roots 看依然可达,因此 GC 无法回收。需要进一步通过工具查看泄漏对象到 GC Roots 的引用链,掌握了泄漏对象的类型信息和 GC Roots 引用链 信息,就可以比较准确地定位出泄漏代码的位置。
内存溢出,表示对象确实还存活着,导致GC 无法回收“有用的”对象,因此就需要检查 堆参数(-Xms 、-Xmx),与机器物理内存对比看是否还可以调大,或者从代码上检查,是否有对象生命周期过长、持有状态时间过长的情况,尝试减少程序运行期的内存消耗。
OutOfMemory简称“OOM”, 直译为“内存耗尽”或“内存溢出”,当然,并不是真的内存耗尽了,它指的是 JVM 的几个逻辑分区的内存不够用了,无法为新的对象分配空间。

在 JVM 的几个主要内存分区中(JVM 栈、本地方法栈、计数器、方法区、Java 堆),只有计数器不会出现这种严重错误,也就是说,我们常说的堆和栈等,都有可能出现 OOM 的问题。

# 当发生内存溢出时dump出当前内存堆的快照-XX:+HeapDumpOnOutOfMemoryError

3.1 堆溢出

由于 Java 中最常见的内存分配就是对象,因此,经常要分配内存用于创建对象的堆区,是出现OOM问题的最常见内存分区。
对于 Java 堆的内存溢出,原因其实非常简单。因为堆是用于存储对象的,因此只要不断地创建对象,并且保证 GC Roots 到对象之间有可达路径避免垃圾回收机制清除这些对象,那么堆就必然会出现 OOM 问题。
要测试 OOM ,只需要将堆的分配大小调的低一些,主要参数:-Xms 最小堆内存 和 -Xmx 最大堆内存。

3.1.1 回收效率不足2%,抛出OutOfMemoryError: GC overhead limit exceeded

本例基于 JDK8 Parallel GC。GC overhead limit 策略用于防止 JVM 长期把绝大部分时间耗在 GC 上却几乎回收不到空间;典型阈值约为 GC 时间超过98%、回收空间不足2%。
溢出代码:

publicclassTuning{publicstaticvoidmain(String[]args)throwsInterruptedException{ArrayList<Long>list=newArrayList<>();longa=0;while(true){list.add(a++);}}}


原因: 本例中一直在发生fullgc中,list集合一直在塞入对象,回收内存占比越来越小,虚拟机中有个规则:垃圾回收(线程)占用超过了98%的资源,但是回收效率不足2%,就会发生了’OOM, GC overhead limit exceeded。
在visualvm中导入生成的Heap快照(默认在项目根路径)


可以看到最占空间的是我们反复添加的Long对象

3.1.2 单次分配对象过大,堆无法容纳,抛出OutOfMemoryError: Java heap space

创建的数组对象超过了10M

3.2 栈溢出

Java 虚拟机规范规定了两种栈异常:
1、如果线程请求的栈深度大于虚拟机所允许的最大深度,抛出 StackOverflowError 异常。
2、如果虚拟机在扩展栈时无法申请到足够的内存空间,抛出 OutOfMemoryError 异常。

3.2.1 死递归,抛出StackOverflowError
publicclassTuning{publicstaticvoidmain(String[]args)throwsInterruptedException{Tuningtuning=newTuning();tuning.test();}publicvoidtest(){test();//死递归}}


在java虚拟机种,Java的栈空间默认是1M大小(不同jdk版本和不同的操作系统这个值会存在差异,可以通过-Xss调整,设置每个线程可使用的内存大小,即栈的大小,一般不用调整),反复调用,会超出栈溢出。在相同物理内存下,减小这个值能生成更多的线程,当然操作系统对一个进程内的线程数还是有限制的,不能无限生成。线程栈的大小是个双刃剑,如果设置过小,可能会出现栈溢出,特别是在该线程内有递归、深层方法调用时出现溢出的可能性更大,如果该值设置过大,就有影响到创建栈的数量,如果是多线程的应用,可能会出现内存溢出的错误。

3.2.2 线程过多,抛出OutOfMemoryError

设置:-XX:+PrintCommandLineFlags -Xmx40m -Xms20m

publicclassTuning{publicstaticvoidmain(String[]args)throwsInterruptedException{while(true){newThread(()->{{try{System.out.println(Thread.currentThread().getName()+"启动");Thread.sleep(10000000);}catch(InterruptedExceptione){}}}).start();}}}

注意:

StackOverflowError:
单个线程调用栈过深,超出该线程栈能够容纳的范围,
常见于无限递归。
OutOfMemoryError: unable to create native thread:
JVM/操作系统已经无法为新线程申请所需的本地资源,
可能与进程可用内存、线程栈大小、系统线程限制等有关,
不能简单理解为“所有虚拟机栈都满了”。

3.3 直接内存溢出,抛出Direct buffer memory

MaxDirectMemorySize最大直接内存默认和堆空间最大内存一致,超过则抛Direct buffer memory异常

publicclassTuning{publicstaticvoidmain(String[]args)throwsInterruptedException{//MaxDirectMemorySize最大直接内存默认和堆空间最大内存一致 也可以通过-XX:MaxDirectMemorySize设置ByteBufferbb=ByteBuffer.allocateDirect(10*1024*1024);}}




3.4 方法区溢出java.lang.OutOfMemoryError: Metaspace

一般发生在动态语言,大量动态生成/加载 Class,或 ClassLoader 长期无法卸载,会导致类元数据不断增长,从而可能产生 OutOfMemoryError: Metaspace。JDK8 后元空间改用本地内存,但并不代表不会溢出。
在经常动态生产大量Class的应用中,CGLIb字节码增强,动态语言,大量JSP(JSP第一次运行需要编译成Java类),基于OSGi的应用(同一个类,被不同的加载器加载也会设为不同的类)。如果方法区内存不够大的话也会发生java.lang.OutOfMemoryError: Metaspace溢出。
jdk1.8后一般不容易出现,不做演示

## jdK1.8以前 设置永久代最小值-XX:PermSize## jdK1.8以前 设置永久代最大值-XX:MaxPermsize# jdk1.8及以后 设置触发元数据GC的初始高水位阈值-XX:MetaspaceSize# jdk1.8及以后 设置元空间最大值-XX:MaxMetaspaceSize# 查看进程触发元数据GC的初始高水位阈值jinfo-flagMetaspaceSize pid# 查看进程最大元空间jinfo-flagMaxMetaspaceSize pid

4. GC日志查看

在虚拟机参数中添加 -Xloggc:gc.log(JDK9后被弃用建议使用-Xlog:gc:gc.log)



也可以添加 -XX:+PrintHeapAtGC(JDK9之前版本适用,之后版本移除) 打印堆信息


可以用日志在线分析来分析GC日志:https://gceasy.io/


GC日志参数:

参数用途
-XX:PrintGC打印GC日志
-XX:+PrintGCDetails打印详细的GC日志,退出前打印堆的详细信息
–XX:+PrintHeapAtGC每次GC前后打印堆信息
-XX:+PrintGCDateStamps打印GC发生的日期
-XX:+PrintGCTimeStamps打印GC发生的时间
-XX:+PrintGCApplicationConcurrentTime打印应用程序的执行时间
-XX:+PrintGCApplicationStoppedTime打印应用由于GC而产生的停顿时间
-XX:+PrintReferenceGC跟踪软引用、弱引用、虚引用和Finallize队列
–XLoggc将GC日志以文件形式输出

5.在线诊断工具Arthas

Arthas 是由阿里巴巴开源的Java诊断工具,主要用于在生产环境中不重启应用的情况下对Java应用进行监控、诊断和分析。其功能比jdk自带工具更强大丰富。

5.1 下载

Arthas官网下载地址

下载压缩包后解压↓


解压后可以看到几个jar包,分别是:
1.arthas-boot.jar
这是启动 Arthas 诊断会话的主要 JAR 文件。它提供了一个用户界面来选择要附加的 Java 进程,并启动诊断会话。
使用方式通常是通过命令行运行:java -jar arthas-boot.jar。运行后,它会列出机器上的所有 Java 进程,让用户选择一个进行诊断。
2.arthas-core.jar
包含了 Arthas 的核心功能,如命令处理、监控和管理等。这个 JAR 在内部被 arthas-boot.jar 使用,用来处理实际的诊断操作。
通常不需要用户直接操作这个 JAR 文件,因为它是由 arthas-boot.jar 在后台调用的。
3.arthas-agent.jar
作为 Java Agent 运行,这个 JAR 文件负责实际附加到目标 Java 进程。它通过使用 Java 的 Instrumentation API 来插入和操纵字节码,从而实现对运行中的 Java 应用的监控和修改。
同样地,这个 JAR 通常是在 arthas-boot.jar 的控制下自动加载的。
4.arthas-client.jar
一个轻量级的命令行客户端,用于连接到已经运行的 Arthas 服务端(即通过 arthas-core.jar 启动的服务)。
可以独立使用,使用户能够从不同的终端或机器远程连接到 Arthas 服务端。
5.arthas-spy.jar
一个非常轻量级的 Java Agent,用于在不修改用户应用的情况下,帮助 arthas-agent.jar 插入到目标 JVM 中。
通常用于一些特殊场景,如当某些 JDK 版本或配置不允许直接加载 arthas-agent.jar 时。
6.math-game.jar
这个 JAR 文件通常用于演示和学习 Arthas 工具的各种功能,因为它是一个简单的数学游戏程序,可以用来实际展示 Arthas 在 Java 应用程序诊断中的用途。

5.2 命令

首先启动服务:

#启动arthas服务java-jararthas-boot.jar


输入对应编号进入对应程序

至此就成功进入了我们要附加的程序。接下来我们逐个讲解下常用命令

5.2.1 dashboard

dashboard,查看当前系统实时数据面板

# 每5秒刷新一次,共10次 -i : 指刷新的时间间隔 -n: 指刷新次数dashboard-i5000-n10

5.2.2 thread

thread命令 显示 Java 进程中所有线程的信息,包括线程ID、名称、所属组、优先级、占用CPU、状态等。
用法:thread 或 thread -n 5(显示前5个线程)

5.2.3 jvm

jvm命令用来显示 JVM 的信息,包括版本、堆内存使用情况、GC 状态等。

5.2.4 trace

trace用于链路追踪,可以查看方法内部调用路径,并输出方法路径上的每个节点上耗

# <class-pattern>:类名模式,支持简单的通配符,如 *Controller。# <method-pattern>:方法名模式,同样支持通配符,如 find*。# -E:开启正则表达式匹配,默认是通配符匹配。# -n <times>:限制结果的数量,默认情况下,trace 命令只追踪到第一个符合条件的方法调用。通过设定 -n 可以修改这个行为,让它追踪多次。# --skipJDKMethod <true|false>:是否跳过 JDK 的方法,默认为 true。trace<class-pattern><method-pattern>

5.2.5 sc

sc -d 类名可以查看类的信息、如类名称、修饰符、是否接口、是否枚举、父类等信息

5.2.6 jad

jad <class-pattern>用来反编译 Java 类

5.2.7 watch

watch命令用来监视变量或表达式的值,并在变化时打印出来

# <class-pattern>: 类名匹配模式,支持通配符。# <method-pattern>: 方法名匹配模式,支持通配符。# <express>: 表达式,用来定义输出的信息,如 params、returnObj、throwExp 等。# [condition-express]: 条件表达式,只有满足条件的调用才会被监控。# [options]: 可选项来控制监控的行为,如监控的次数等。# OGNL表达式来访问方法的参数、返回值、抛出的异常等信息:# params: 获取所有参数,params[0], params[1] 等来获取具体的参数。# returnObj: 获取方法的返回值。# throwExp: 获取抛出的异常。# target: 获取被监控的对象,即方法所在的对象实例。# clazz: 获取类的Class对象,即方法所在的类watch<class-pattern><method-pattern><express>[condition-express][options]

5.2.8 getstatic

getstatic命令用于查看一个类的静态字段的值。这个命令特别有用于调试时,需要检查静态全局状态或者配置的情况。使用格式如下↓

# class-pattern: 类名模式。# field-pattern: 字段名模式。getstatic[class-pattern][field-pattern]

5.2.9 OGNL

ognl命令是一种表达式语言,允许你在运行时操作对象、调用方法或者修改字段等。格式如下↓

# 访问静态字段ognl'@class@field'# 调用静态方法ognl'@class@method()'# 访问对象字段ognl'object.field'# 调用对象方法ognl'object.method()'

5.3 火焰图

Arthas 本身支持 Windows,但 profiler 基于 async-profiler,而 async-profiler 官方目前主要支持 Linux 和 macOS,因此原生 Windows 环境通常无法直接使用 Arthas profiler 生成火焰图。此处略过

6.离线内存分析Eclipse MAT

前面的 Arthas 更适合:

程序正在运行时

进行在线诊断,比如:

  • 查看线程状态
  • 查看方法耗时
  • 观察参数和返回值
  • 排查接口慢、线程阻塞等问题

而如果已经出现:

  • 内存持续上涨
  • Full GC 频繁
  • OOM
  • 已经导出了.hprof堆快照文件

这时候更适合使用:

Eclipse MAT(Memory Analyzer Tool)

它是一款专门用于:

离线分析 Java Heap Dump

的工具。

MAT 非常适合排查:

  • 内存泄漏
  • 大对象占用
  • 哪些对象数量最多
  • 哪些对象占内存最大
  • 谁持有对象引用导致无法被 GC 回收
  • 对象到 GC Roots 的引用链

简单理解:

jmap / jcmd ↓ 负责导出 heap dump Eclipse MAT ↓ 负责分析 heap dump

6.0 下载安装

下载地址:https://eclipse.dev/mat/download/

解压后打开

6.1 MAT的作用

MAT 最常见的几个能力如下:

  • Histogram(对象直方图)
    查看各类对象的数量和占用内存。

  • Dominator Tree(支配树)
    找出哪些对象“支配”了大量内存,也就是哪些对象一旦释放,可以连带释放最多内存。

  • Retained Heap(保留堆)
    分析某个对象真正“拖住”了多少内存。

  • Path to GC Roots
    查看对象为什么没有被回收,它是通过哪条引用链一直可达的。

  • Leak Suspects Report
    自动生成内存泄漏嫌疑分析报告。

因此:

Arthas 更擅长排查“程序正在运行时发生了什么”,而 MAT 更擅长排查“堆内存里到底有什么,以及为什么无法被回收”。


6.2 MAT的使用流程

MAT 的典型使用流程如下:

程序出现 OOM 或内存异常 ↓ 使用 jmap 或 jcmd 导出 heap dump ↓ 使用 MAT 打开 .hprof 文件 ↓ 查看 Leak Suspects Report ↓ 查看 Histogram / Dominator Tree ↓ 分析大对象和引用链 ↓ 定位导致内存无法释放的代码

6.3 导出Heap Dump

使用 jmap 导出

jmap-dump:format=b,file=heap.hprof pid

或者使用 jcmd 导出(推荐)

jcmd pid GC.heap_dump heap.hprof

执行完成后,会在当前目录生成:

heap.hprof

生产环境注意:无论使用 jmap 还是 jcmd 生成 Heap Dump,都属于较高影响的诊断操作。导出过程中可能产生较长 STW,耗时与堆大小、对象数量和磁盘写入速度有关。jmap -dump:live 通常会进行完整 GC 以筛选存活对象;现代 HotSpot 的 jcmd GC.heap_dump 默认也会请求一次 Full GC,使用 -all 时则会包含不可达对象而不先请求该 Full GC。大堆服务执行前应评估停顿时间和磁盘空间。

jmap-dump:format=b,file=heap.hprof<pid>→ 全部对象,不加 live jmap -dump:live,format=b,file=heap.hprof<pid>→ 仅存活对象,通常伴随 Full GC jcmd<pid>GC.heap_dump heap.hprof → 默认仅存活对象,并请求 Full GC jcmd<pid>GC.heap_dump-allheap.hprof → 包含不可达对象,不先请求 Full GC

6.4 打开Heap Dump

打开 MAT 后,选择:

File → Open Heap Dump

然后选择导出的:

heap.hprof

第一次打开时,MAT 可能会提示是否生成:

Leak Suspects Report

建议直接生成。


因为这个报告对于初学者非常友好,通常可以快速帮助我们定位:

  • 哪类对象占用了最多内存
  • 是否存在明显的内存泄漏嫌疑
  • 哪个对象链路最值得重点关注

6.5 MAT中最常用的几个视图

6.5.1 Histogram

Histogram 可以理解为:

对象统计表

可以看到:

  • 类名
  • 实例数量
  • Shallow Heap
  • Retained Heap

其中:

  • Shallow Heap:对象本身占用的内存
  • Retained Heap:如果这个对象被回收,理论上可以一起释放的总内存

如果某个类的实例数量特别多,或者 Retained Heap 特别大,就值得重点分析。


6.5.2 Dominator Tree

Dominator Tree(支配树)是 MAT 中最有用的视图之一。

它的核心思想是:

如果一个对象被释放,可以连带释放哪些对象?

因此在 Dominator Tree 中,通常可以快速找到:

  • 哪个集合对象持有了大量元素
  • 哪个缓存对象拖住了大块内存
  • 哪个静态变量引用了大量对象

实际排查内存泄漏时,往往先看:

Dominator Tree

再逐层展开分析。


6.5.3 Path to GC Roots

如果发现某个对象本来业务上已经“没用了”,却仍然没有被回收,这时就可以使用:

Path to GC Roots

查看它是通过哪条引用链仍然可达。

这一步非常关键,因为它能够回答一个核心问题:

为什么 GC 没有回收它?

常见情况包括:

  • 被静态变量引用
  • 被集合类长期持有
  • 被 ThreadLocal 持有
  • 被某个缓存对象引用
  • 被类加载器或框架上下文持有

6.6 使用案例:分析集合对象导致的内存占用

下面写一个简单示例,模拟对象一直被集合持有,导致内存不断增长。

importjava.util.ArrayList;importjava.util.List;publicclassMatDemo{staticList<byte[]>list=newArrayList<>();publicstaticvoidmain(String[]args)throwsException{while(true){list.add(newbyte[1024*1024]);// 每次添加1MBThread.sleep(200);}}}

运行时给一个较小堆:

-Xms32m-Xmx32m-XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=heap.hprof

一段时间后程序很容易出现:

java.lang.OutOfMemoryError: Java heap space

这时会生成 heap dump:

然后使用 MAT 打开。


6.7 案例分析思路

打开 MAT 后,可以按以下顺序分析:

第一步:查看 Leak Suspects Report(打开时勾选了就会直接打开,右侧的default_report就是)

如果 MAT 已经自动发现明显问题,通常会提示:

  • 某个ArrayList占用大量内存
  • 某个静态字段一直持有对象

这一步适合快速定位方向。


第二步:查看 Histogram

Histogram 显示,byte[] 是当前堆中占用内存最大的对象类型,共 437 个实例,占用约 28.13MB。其中 Leak Suspects 已经定位到的 28 个约 1MB 的 byte[] 就占用了约 28MB,说明本次 OOM 的主要直接内存消费者正是这些大字节数组。


第三步:查看 Dominator Tree

Dominator Tree 展开后可以看到:
class MatDemo → static list → ArrayList → elementData(Object[]) → 多个 byte[1048576]

说明本次 OOM 的直接内存消费者是大量 byte[],
而真正支配这些对象、导致它们无法被释放的关键引用链,
是 MatDemo 中的静态集合 list。


第四步:查看 Path to GC Roots

Path to GC Roots 与 Merge Shortest Paths to GC Roots 的区别
功能主要用途更适合的场景
Path to GC Roots分析某个具体对象为什么仍然可以从 GC Roots 到达已经找到一个可疑对象,希望追踪它的完整引用来源
Merge Shortest Paths to GC Roots将多个对象到 GC Roots 的最短路径合并,找出它们共同的引用来源某一类对象数量很多,希望分析“是谁共同持有了这一批对象”

6.8 结合案例总结MAT排查步骤

通过上面的案例,可以总结出一套常用的 MAT 排查思路:

1. 先看 Leak Suspects Report 2. 再看 Histogram,找数量多、占用大的对象 3. 再看 Dominator Tree,找真正“拖住”大块内存的对象 4. 最后用 Path to GC Roots 分析引用链

也就是说,MAT 的重点不是只看:

哪些对象多

更重要的是看:

谁在持有这些对象 为什么这些对象没有被回收 释放哪个对象能一次性回收最多内存

6.9 MAT与Arthas的区别

可以简单对比如下:

工具定位适合场景
Arthas在线动态诊断方法耗时、线程阻塞、参数观察、线上问题定位
Eclipse MAT离线内存分析OOM、内存泄漏、Heap Dump 分析、GC Roots 引用链

所以:

接口慢、线程卡顿 ↓ 优先考虑 Arthas 内存上涨、OOM、怀疑内存泄漏 ↓ 优先考虑 MAT

二者并不是互相替代,而是互相补充。


6.10 小结

Eclipse MAT 是分析 Java 堆快照最常用的工具之一。

它特别适合:

  • 分析.hprof
  • 排查 OOM
  • 定位内存泄漏
  • 查看 Dominator Tree
  • 查看对象到 GC Roots 的引用链

其核心排查思路可以概括为:

先看谁占内存最多 再看谁真正持有这些对象 最后找到为什么 GC 无法回收

如果说 Arthas 更偏:

程序正在运行时的在线诊断

那么 MAT 更偏:

问题发生后的离线内存分析

在实际开发中,这两类工具通常需要结合使用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询