MAT(堆分析/内存泄漏排查)
MAT:堆转储分析的利器
MAT(Memory Analyzer Tool)是 Eclipse 提供的专业堆转储分析工具,专门用于排查内存泄漏和内存占用问题。它能处理数 GB 的大堆转储,分析能力远超 jhat 和 VisualVM 自带的 HeapWalker。
安装与启动
安装方式
bash
# 方式一:独立安装(推荐)
# 下载地址:https://eclipse.dev/mat/downloads/
# 下载 mat-macosx-cocoa-aarch64_*.tar.gz 或对应版本
tar -xzf mat-macosx-cocoa-aarch64_*.tar.gz
./mat/MemoryAnalyzer
# 方式二:Eclipse 插件
# Help → Eclipse Marketplace → 搜索 "Memory Analyzer"
# 方式三:命令行版本(无需 GUI)
# MAT 也提供命令行工具,适合服务器环境
./ParseHeapDump.sh heap.hprof org.eclipse.mat.api:suspects内存配置
处理大堆转储时,建议增加 MAT 内存:
bash
# 修改 MemoryAnalyzer.ini
# 内存建议为堆转储文件的 1/3 到 1/2
-vmargs
-Xmx4g # 根据堆转储大小调整
-XX:+UseG1GC打开堆转储文件
支持的格式
MAT 支持的堆转储格式:
1. .hprof → 标准 HPROF 格式(jmap、jcmd 生成)
2. .phd → IBM Portable Heap Dump
3. .dprof → DTFJ 格式(IBM JVM)
4. .yarp → SAP JVM 格式打开方式
bash
# 方式一:MAT 界面打开
# File → Open Heap Dump → 选择 .hprof 文件
# 方式二:命令行打开
MemoryAnalyzer -hprofPath heap.hprof
# 方式三:指定报告类型
./ParseHeapDump.sh heap.hprof org.eclipse.mat.api:suspects概览仪表盘(Overview)
打开堆转储后,首先看到的是 Overview 页面:
Overview 页面包含:
1. Leak Suspects → 内存泄漏嫌疑人(自动分析)
2. Top Components → 占用最多的组件
3. Biggest Objects → 最大的对象
4. Actions → 常用分析操作
5. Step-by-Step Tutorials → 引导式分析Leak Suspects(泄漏嫌疑人)
MAT 的自动分析会列出可疑的内存泄漏区域:
Leak Suspects 报告示例:
---
1. One instance of "com.example.Cache" loaded by
"<system class loader>" occupies 512,345,678 bytes.
...
→ 点击「Details」查看详细信息
2. The thread java.lang.Thread @ 0x12345678 keeps
causing OutOfMemoryError...
→ 线程持有的对象导致的 OOMHistogram(直方图)
Histogram 是最基础的视图,显示每个类的实例数量和内存占用。
基本用法
查看方式:
导航栏 → Java Basics → Histogram
显示内容:
Class Name | 对象数量 | Shallow Heap | Retained Heap
java.lang.String | 45,678 | 2,678,901 | 15,234,567
char[] | 23,456 | 4,567,890 | 10,123,456
com.example.User | 1,234 | 456,789 | 2,345,678关键概念:Shallow Heap vs Retained Heap
Shallow Heap(浅堆):
→ 对象本身占用的内存
→ 包含:对象头 + 实例字段
Retained Heap(深堆):
→ 对象本身 + 所有可达对象的总和
→ 如果对象被 GC,可释放的总内存
→ 包含了对象引用的子树
示例:
User 对象 → shallow: 100B, retained: 500KB
(User 对象本身 100B,
持有的 List 中所有对象 499.9KB)常用操作
Histogram 常用操作:
1. 按内存排序
→ 点击「Retained Heap」列排序
2. 搜索类
→ 搜索框输入类名
3. 过滤
→ 右键 → Show Objects by Class → with class loader
→ 右键 → List Objects → with incoming references
4. 正则过滤
→ 支持 Java 正则表达式,如 "java\\.util\\..*"Dominator Tree(支配树)
Dominator Tree 是 MAT 中最重要的视图之一,用于快速定位大内存占用者。
什么是支配关系
支配关系定义:
对象 A 支配对象 B,当且仅当:
所有从 GC Root 到 B 的路径都经过 A
支配树的特点:
→ 如果 A 被 GC,所有被 A 支配的对象都会被回收
→ 树的根节点通常是 GC Root
→ 子节点是被父节点直接/间接支配的对象常用操作
Dominator Tree 视图:
Class Name | Retained Heap | Percentage
com.example.Cache | 512,345,678 | 45.2%
└─ java.util.HashMap | 456,789,012 | 40.3%
└─ java.util.HashMap$Entry[2048] | 345,678,901 | 30.5%
操作:
1. 按 Retained Heap 排序
2. 展开树节点查看子节点
3. 右键 → View → Path to GC Roots
4. 右键 → Merge Shortest Paths to GC RootsGC Root 路径分析
Path to GC Roots(到 GC Root 的路径):
→ 从对象出发,追溯到 GC Root 的最短路径
→ 用于判断对象为什么不能被回收
GC Root 类型:
1. Class → 由 Bootstrap ClassLoader 加载的类
2. Thread → 活动线程
3. Stack Local → 栈上的局部变量
4. Monitor → 持有监视器锁的对象
5. Native Static → 静态变量Top Consumers(最大内存消费者)
Top Consumers 自动列出内存占用的主要区域:
Top Consumers 报告:
1. by Class → 按类统计内存占用
2. by Package → 按包统计
3. by ClassLoader → 按类加载器统计
Biggest Objects → 按单个对象大小排序
对象实例 | Retained Heap
byte[1024000] | 1,000,000
com.example.BigCache | 500,000
char[500000] | 500,000OQL(对象查询语言)
OQL 允许用 SQL-like 语法查询堆:
基本语法
sql
SELECT * FROM java.lang.String
SELECT * FROM char[] WHERE length > 1000
SELECT * FROM com.example.MyClass WHERE field != null常用查询示例
sql
-- 查找所有 String 内容包含 "error" 的实例
SELECT s.value.toString() FROM java.lang.String s
WHERE s.value.toString().contains("error")
-- 查找大于 1MB 的数组
SELECT * FROM array.* WHERE length > 1048576
-- 查找持有大量内存的 HashMap
SELECT * FROM java.util.HashMap
WHERE size() > 10000
-- 查找线程局部变量
SELECT * FROM java.lang.ThreadLocal$ThreadLocalMap
-- 查找特定类加载器加载的所有类
SELECT * FROM java.lang.Class
WHERE classLoader.class.name.toString() = "org.apache.catalina.loader.WebappClassLoader"
-- 查找 String.intern() 过的不该存在的字符串
SELECT * FROM java.lang.String s WHERE s.count > 100OQL 辅助函数
sql
-- toHexString: 转换为十六进制地址
SELECT toHexString(addr) FROM com.example.MyClass
-- objects to Retained Heap
-- 统计某个类的总 retained heap
SELECT sum(retainedHeap(obj)) FROM "com.example.MyClass"
-- 按 retained heap 排序
SELECT * FROM com.example.MyClass
ORDER BY retainedHeap(this) DESC
-- 查看对象字段
SELECT fields(this) FROM com.example.MyClass内存泄漏分析实战
场景一:Cache 导致内存泄漏
bash
# 1. 生成堆转储
jmap -dump:live,format=b,file=heap.hprof <pid>
# 2. 用 MAT 打开
MemoryAnalyzer -hprofPath heap.hprof
# 3. 查看 Leak Suspects
# Overview → Leak Suspects → 点击 Details
# 4. 查看 Histogram
# Java Basics → Histogram
# 按 Retained Heap 排序
# 找到 Cache 相关的类(如 ConcurrentHashMap)
# 5. 查看 Dominator Tree
# Java Basics → Dominator Tree
# 展开 Cache 对象
# 查看持有的数据量
# 6. 追溯 GC Root
# 右键 Cache 对象 → Path to GC Roots → exclude weak/soft references
# 分析为什么 Cache 没有被回收:
# - 静态变量持有?
# - 线程局部变量持有?
# - 监听器/回调持有?
# 7. 定位泄漏原因
# 常见原因:
# - 静态 Map 无上限增长
# - Listener 没有正确移除
# - ThreadLocal 没有清理场景二:线程导致的 OOM
bash
# 1. 查看 OOM 时的堆转储
# 或者:jmap -dump:live,format=b,file=heap.hprof <pid>
# 2. 查看 Leak Suspects
# Overview → Leak Suspects
# 如果报告指出某个线程导致 OOM
# 3. 查看线程
# Java Basics → Thread Overview
# 或:Java Basics → Thread Details
# 4. 查看线程持有的对象
# 选择具体线程 → 查看其 Stack Frames
# 查看局部变量引用的对象
# 5. 分析线程栈
# System Thread → java.lang.Thread
# 查看状态和堆栈场景三:类加载器泄漏
bash
# 场景:应用反复部署后内存持续增长(类加载器泄漏)
# 1. 查看 Histogram
# 按 ClassLoader 分组
# 右键 → Group By → Class Loader
# 2. 查看重复的类加载器
# 同一类加载器加载的类数量是否过多?
# 3. 查看对象
# 选择类加载器 → List Objects → with outgoing references
# 查看该加载器加载的所有类
# 4. 追溯 GC Root
# 右键 → Path to GC Roots
# 找出什么持有类加载器导致无法卸载快捷操作
MAT 常用快捷键:
Ctrl + F → 查找
Ctrl + I → 检查对象
Ctrl + L → OQL 控制台
Ctrl + 1 → Quick Fix
F5 → 刷新
常用右键菜单:
Path to GC Roots → 追溯 GC Root 路径
Merge Shortest Paths → 合并多条路径
List Objects → 列出对象
Compare to Another Heap Dump → 对比两个堆转储本节小结
MAT 核心功能速查:
| 功能 | 用途 | 入口 |
|---|---|---|
| Leak Suspects | 自动分析内存泄漏嫌疑人 | Overview |
| Histogram | 按类统计实例数和内存 | Java Basics |
| Dominator Tree | 按支配关系显示内存占用 | Java Basics |
| Top Consumers | 最大内存消费者排名 | Reports |
| OQL | SQL-like 堆查询 | 工具栏 OQL |
| Thread Overview | 线程及持有对象 | Java Basics |
使用技巧:先看 Leak Suspects 快速定位,再通过 Histogram/Dominator Tree 深入分析,最后用 Path to GC Roots 追溯原因。
下一节,我们来看 JProfiler(安装/内存/CPU/线程分析)。
