JVM 内存泄露排查:MAT 工具实战,揪出那个吃内存的"怪兽"
JVM 内存泄露排查:MAT 工具实战,揪出那个吃内存的”怪兽”
一、概述
1.1 背景介绍
在 Java 应用的生产环境中,内存泄露(Memory Leak)是最常见也最难排查的问题之一。随着应用运行时间增长,堆内存持续增加,最终导致频繁 Full GC 甚至 OOM(Out Of Memory)崩溃。传统的日志分析和监控难以定位具体的泄露对象,而 Eclipse Memory Analyzer(MAT)作为专业的堆转储分析工具,能够快速定位内存泄露的根本原因,帮助开发人员精准找到那些”占着茅坑不拉屎”的对象。
1.2 技术特点
-
• 强大的分析能力:支持超大堆转储文件(GB级别)的快速分析,自动识别内存泄露嫌疑点 -
• 可视化诊断:提供直观的对象引用链、支配树、直方图等多维度视图 -
• 智能报告:自动生成 Leak Suspects Report,快速定位问题根源 -
• 扩展性强:支持 OQL(Object Query Language)查询和插件扩展
1.3 适用场景
-
• 场景一:生产环境频繁出现 Full GC,应用响应缓慢甚至假死 -
• 场景二:监控显示堆内存持续增长,无法回收,最终触发 OOM -
• 场景三:应用运行一段时间后性能下降明显,需要定期重启才能恢复 -
• 场景四:开发阶段的内存优化和泄露预防
1.4 环境要求
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
二、详细步骤
2.1 准备工作
◆ 2.1.1 获取堆转储文件
方式一:主动触发(推荐生产环境使用)
# 方法1: 使用 jmap 生成堆转储jmap -dump:live,format=b,file=/tmp/heap_dump.hprof <pid># 方法2: 使用 jcmd (JDK 8+推荐)jcmd <pid> GC.heap_dump /tmp/heap_dump.hprof# 方法3: 使用 jvisualvm 图形化工具触发# 打开 jvisualvm -> 选择进程 -> Monitor -> Heap Dump
方式二:OOM时自动生成
# 在JVM启动参数中添加java -XX:+HeapDumpOnOutOfMemoryError \ -XX:HeapDumpPath=/var/log/java/heap_dump.hprof \ -jar your-application.jar
说明:
-
• live参数会先触发一次 Full GC,过滤掉已经死亡的对象,推荐使用 -
• 堆转储过程会触发 STW(Stop The World),建议在低峰期操作 -
• 生成的 hprof 文件大小约等于堆内存使用量,需确保磁盘空间充足
◆ 2.1.2 安装 MAT 工具
# Linux/Mac 下载并解压wget https://www.eclipse.org/downloads/download.php?file=/mat/1.14.0/rcp/MemoryAnalyzer-1.14.0-linux.gtk.x86_64.zipunzip MemoryAnalyzer-1.14.0-linux.gtk.x86_64.zipcd mat./MemoryAnalyzer# Windows: 直接下载安装包安装# https://www.eclipse.org/mat/downloads.php
调整 MAT 内存配置
# 编辑 MemoryAnalyzer.ini-vmargs-Xmx16g# 设置为堆转储文件大小的1.5倍
2.2 核心分析流程
◆ 2.2.1 导入堆转储文件
# 启动 MAT 后File -> Open Heap Dump -> 选择 heap_dump.hprof# 选择分析报告类型(首次分析推荐)Leak Suspects Report
说明:MAT 会自动解析堆转储文件并生成索引,第一次打开较慢,后续会使用缓存加速。
◆ 2.2.2 Leak Suspects Report 分析
MAT 自动生成的泄露嫌疑报告会标注出最可能的内存泄露点:
报告解读要点:
-
• Problem Suspect 1/2/3:MAT 识别出的前3个最可疑的内存泄露点 -
• Shortest Paths To the Accumulation Point:从 GC Root 到泄露对象的最短引用链 -
• Accumulated Objects by Class:被大量累积的对象类型统计
示例分析:
Problem Suspect 1----------------The thread "http-nio-8080-exec-23" keeps local variables with total size 2,145,678,912 bytes.Suspects:- One instance of "java.util.HashMap" loaded by "<system class loader>" occupies 2,045,123,456 (95.32%) bytes.Keywords:java.util.HashMapcom.example.cache.LocalCache
◆ 2.2.3 使用 Dominator Tree(支配树)
Toolbar -> Dominator Tree
操作步骤:
-
1. 按 Retained Heap 排序(默认),找到占用内存最多的对象 -
2. 右键目标对象 -> Path to GC Roots -> exclude weak/soft references -
3. 分析引用链,找到持有该对象的根本原因
参数说明:
-
• Shallow Heap:对象自身占用内存大小 -
• Retained Heap:对象被回收后可释放的总内存(包含引用对象) -
• Percentage:占总堆内存的百分比
◆ 2.2.4 Histogram(直方图)分析
Toolbar -> Histogram
查找可疑对象:
# 右键列表 -> Group by package# 快速定位业务代码中的对象# 查看某个类的所有实例右键 com.example.User -> List objects -> with incoming references
2.3 高级分析技巧
◆ 2.3.1 OQL 查询
-- 查找所有 size > 10000 的 ArrayListSELECT*FROM java.util.ArrayList WHERE size() >10000-- 查找特定包下的所有对象SELECT*FROM com.example.model.*WHERE (toString() LIKE ".*leaked.*")-- 查找持有大量对象的 MapSELECT s, s.size FROM java.util.HashMap s WHERE s.size >1000-- 查找特定字段值的对象SELECT*FROM com.example.User WHERE name.toString() = "admin"
◆ 2.3.2 线程分析
Toolbar -> Thread Overview
查看线程栈持有的对象:
右键线程 -> List objects-> with incoming references # 查看被哪些对象引用-> with outgoing references # 查看引用了哪些对象
◆ 2.3.3 对比分析
# 分析两个不同时间点的堆转储# 1. 打开第一个 hprof 文件# 2. 工具栏 -> Compare To Another Heap Dump# 3. 选择第二个 hprof 文件# 对比结果会显示:- 新增对象- 增长最快的对象- 可能的泄露点
三、示例代码和配置
3.1 完整配置示例
◆ 3.1.1 JVM 启动参数配置
# 生产环境推荐配置java -Xms4g -Xmx4g \ -XX:+HeapDumpOnOutOfMemoryError \ -XX:HeapDumpPath=/var/log/java/heap_dump_%p_%t.hprof \ -XX:+UseG1GC \ -XX:MaxGCPauseMillis=200 \ -XX:+PrintGCDetails \ -XX:+PrintGCDateStamps \ -Xloggc:/var/log/java/gc_%p.log \ -XX:+UseGCLogFileRotation \ -XX:NumberOfGCLogFiles=5 \ -XX:GCLogFileSize=20M \ -jar your-application.jar
◆ 3.1.2 内存泄露监控脚本
#!/bin/bash# 文件名: monitor_memory_leak.sh# 功能: 定期检测JVM内存使用,超过阈值自动生成堆转储APP_NAME="your-application"HEAP_THRESHOLD=85 # 堆内存使用超过85%触发DUMP_DIR="/var/log/heap_dumps"LOG_FILE="/var/log/memory_monitor.log"mkdir -p "$DUMP_DIR"whiletrue; do# 获取Java进程ID PID=$(pgrep -f "$APP_NAME")if [ -z "$PID" ]; thenecho"[$(date)] 应用未运行" >> "$LOG_FILE"sleep 60continuefi# 获取堆内存使用率 HEAP_USAGE=$(jstat -gcutil "$PID" | tail -1 | awk '{print $4}')echo"[$(date)] PID: $PID, 老年代使用率: ${HEAP_USAGE}%" >> "$LOG_FILE"# 判断是否超过阈值if (( $(echo "$HEAP_USAGE > $HEAP_THRESHOLD" | bc -l) )); then TIMESTAMP=$(date +%Y%m%d_%H%M%S) DUMP_FILE="${DUMP_DIR}/heap_dump_${PID}_${TIMESTAMP}.hprof"echo"[$(date)] 触发堆转储: $DUMP_FILE" >> "$LOG_FILE"# 生成堆转储 jcmd "$PID" GC.heap_dump "$DUMP_FILE"# 发送告警(集成你的告警系统)# curl -X POST "http://alert.example.com/api/alert" \# -d "message=堆内存使用率超过${HEAP_THRESHOLD}%,已生成堆转储"# 生成后等待30分钟再检测sleep 1800fisleep 300 # 每5分钟检测一次done
3.2 实际应用案例
◆ 案例一:ThreadLocal 导致的内存泄露
场景描述:Web 应用使用 ThreadLocal 存储用户上下文,但在请求结束后未正确清理,导致 Tomcat 线程池中的线程持续持有大量用户数据。
问题代码:
publicclassUserContextHolder {privatestaticfinal ThreadLocal<UserContext> context = newThreadLocal<>();publicstaticvoidsetContext(UserContext user) { context.set(user); // 设置用户上下文 }publicstatic UserContext getContext() {return context.get(); }// 问题:缺少 remove() 方法调用}// Controller 中使用@RestControllerpublicclassUserController {@GetMapping("/api/user")public User getUser() {UserContextctx=newUserContext(); ctx.setUserData(loadLargeUserData()); // 加载大量数据 UserContextHolder.setContext(ctx);// 业务逻辑...// 问题:请求结束后未清理 ThreadLocalreturn user; }}
MAT 分析步骤:
-
1. 打开堆转储文件 -> 查看 Leak Suspects Report -
2. 发现问题:Tomcat 工作线程持有大量 ThreadLocalMap对象 -
3. Dominator Tree -> 找到 Thread对象 -> 展开threadLocals -
4. Path to GC Roots -> 发现引用链: Thread "http-nio-8080-exec-1"-> threadLocals (ThreadLocalMap)-> Entry[]-> Entry.value (UserContext)-> userData (byte[1048576]) // 1MB 用户数据
修复方案:
publicclassUserContextHolder {privatestaticfinal ThreadLocal<UserContext> context = newThreadLocal<>();publicstaticvoidsetContext(UserContext user) { context.set(user); }publicstatic UserContext getContext() {return context.get(); }// 添加清理方法publicstaticvoidclear() { context.remove(); }}// 使用 Filter 或拦截器统一清理@ComponentpublicclassUserContextFilterimplementsFilter {@OverridepublicvoiddoFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {try { chain.doFilter(request, response); } finally {// 请求结束后清理 ThreadLocal UserContextHolder.clear(); } }}
◆ 案例二:静态集合缓存导致的泄露
场景描述:为了提升性能,使用静态 HashMap 缓存查询结果,但缺少过期机制,导致缓存无限增长。
问题代码:
publicclassUserCache {// 问题:静态缓存,永远不会被GCprivatestaticfinal Map<Long, User> USER_CACHE = newHashMap<>();public User getUser(Long id) {if (!USER_CACHE.containsKey(id)) {Useruser= userRepository.findById(id); USER_CACHE.put(id, user); // 持续添加,永不删除 }return USER_CACHE.get(id); }}
MAT 分析结果:
Histogram 视图:Class Name | Objects | Shallow Heap | Retained Heap------------------------------------|---------|--------------|---------------java.util.HashMap$Node | 1,234,567 | 59 MB | 2.3 GBcom.example.model.User | 1,234,567 | 48 bytes | 1.8 GBcom.example.cache.UserCache | 1 | 24 bytes | 2.3 GB
修复方案:
import com.github.benmanes.caffeine.cache.Cache;import com.github.benmanes.caffeine.cache.Caffeine;publicclassUserCache {// 使用带过期机制的缓存privatestaticfinal Cache<Long, User> USER_CACHE = Caffeine.newBuilder() .maximumSize(10_000) // 最大缓存数量 .expireAfterWrite(Duration.ofHours(1)) // 1小时后过期 .expireAfterAccess(Duration.ofMinutes(30)) // 30分钟未访问则过期 .build();public User getUser(Long id) {return USER_CACHE.get(id, k -> userRepository.findById(k)); }}
◆ 案例三:事件监听器未注销
场景描述:动态注册事件监听器,但在对象销毁时未注销,导致监听器和相关对象无法被回收。
问题代码:
publicclassDataProcessor {privatefinal List<byte[]> dataBuffer = newArrayList<>();publicDataProcessor(EventBus eventBus) {// 注册监听器 eventBus.register(this); // 问题:EventBus 持有 this 引用 }@SubscribepublicvoidhandleEvent(DataEvent event) { dataBuffer.add(event.getData()); // 持续累积数据 }// 问题:对象销毁时未调用 eventBus.unregister(this)}// 使用场景for (inti=0; i < 1000; i++) {DataProcessorprocessor=newDataProcessor(eventBus);// processor 使用完毕,期望被GC回收// 但由于 EventBus 持有引用,无法回收}
修复方案:
publicclassDataProcessorimplementsAutoCloseable {privatefinal EventBus eventBus;privatefinal List<byte[]> dataBuffer = newArrayList<>();publicDataProcessor(EventBus eventBus) {this.eventBus = eventBus; eventBus.register(this); }@SubscribepublicvoidhandleEvent(DataEvent event) { dataBuffer.add(event.getData()); }@Overridepublicvoidclose() {// 注销监听器,释放引用 eventBus.unregister(this); dataBuffer.clear(); }}// 使用 try-with-resources 自动清理try (DataProcessorprocessor=newDataProcessor(eventBus)) {// 使用 processor} // 自动调用 close()
四、最佳实践和注意事项
4.1 最佳实践
◆ 4.1.1 性能优化
-
• 优化点一:减少堆转储文件大小 # 使用 live 参数,只转储存活对象jmap -dump:live,format=b,file=heap.hprof <pid># 或者先手动触发Full GCjcmd <pid> GC.runjcmd <pid> GC.heap_dump heap.hprof -
• 优化点二:增量分析 -
• 先生成基准堆转储(T0) -
• 运行一段时间后生成第二个堆转储(T1) -
• 使用 MAT 的 Compare 功能对比差异 -
• 重点关注新增和增长的对象 -
• 优化点三:分段分析大文件 # 对于超大堆转储(>50GB),可以使用命令行模式./ParseHeapDump.sh heap.hprof org.eclipse.mat.api:suspects# 或者使用 jhat 进行初步筛选jhat -J-Xmx16g heap.hprof# 访问 http://localhost:7000 查看初步分析
◆ 4.1.2 安全加固
-
• 安全措施一:保护敏感信息 # 堆转储可能包含密码等敏感信息# 生成后立即加密openssl enc -aes-256-cbc -salt -in heap.hprof -out heap.hprof.enc# 分析时解密openssl enc -d -aes-256-cbc -in heap.hprof.enc -out heap.hprof -
• 安全措施二:限制堆转储文件访问权限 # 设置严格的文件权限chmod 600 /var/log/java/*.hprofchown app:app /var/log/java/*.hprof -
• 安全措施三:定期清理历史堆转储 # 只保留最近7天的堆转储文件find /var/log/java -name "*.hprof" -mtime +7 -delete
◆ 4.1.3 预防性监控
-
• 监控指标: -
• 老年代(Old Gen)使用率持续 > 80% -
• Full GC 频率 > 1次/分钟 -
• Full GC 后堆内存回收率 < 30% -
• 应用响应时间 P99 持续上涨 -
• 监控脚本:
#!/bin/bash# 实时监控GC情况jstat -gcutil <pid> 1000 10 | awk 'NR>1 { if ($4 > 80) { print "警告: 老年代使用率 " $4 "%" } if ($6 > 10) { print "警告: Full GC次数 " $6 }}'
4.2 注意事项
◆ 4.2.1 配置注意事项
⚠️ 警告:生成堆转储会触发 STW,生产环境需谨慎操作
-
• ❗ 注意事项一:堆转储期间应用完全停止响应,大堆(>10GB)可能持续数分钟,建议: -
• 在负载均衡中摘除节点后再操作 -
• 选择业务低峰期执行 -
• 使用 jcmd代替jmap,性能更好 -
• ❗ 注意事项二:确保磁盘空间充足,至少为堆内存大小的 1.5 倍 # 操作前检查磁盘空间df -h /var/log/java -
• ❗ 注意事项三:MAT 分析需要大量内存,确保分析机器有足够资源 # MemoryAnalyzer.ini 配置-Xmx32g # 设置为堆转储大小的1.5-2倍
◆ 4.2.2 常见错误
|
|
|
|
|---|---|---|
|
|
|
-Xmx 或使用 jmap -F 强制转储 |
|
|
|
-Xmx |
|
|
|
jhat -J-Xmx8g heap.hprof 验证 |
|
|
dump:live 参数 |
|
|
|
|
|
|
|
|
-XX:+UnlockDiagnosticVMOptions |
◆ 4.2.3 兼容性问题
-
• 版本兼容:MAT 版本需与 JDK 版本匹配 -
• JDK 8: MAT 1.10+ -
• JDK 11: MAT 1.11+ -
• JDK 17: MAT 1.13+ -
• 平台兼容:不同 JVM 的堆转储格式略有差异 -
• HotSpot VM: 完全支持 -
• OpenJ9: 需要使用 PHD 格式 -
• GraalVM: 需要使用 –vm.XX:+HeapDumpOnOutOfMemoryError -
• 格式兼容: # 确认堆转储格式file heap.hprof# 输出: heap.hprof: Java hprof dump, binary format
五、故障排查和监控
5.1 故障排查
◆ 5.1.1 日志查看
# 查看 GC 日志,分析内存回收情况tail -f /var/log/java/gc.log | grep "Full GC"# 分析 GC 日志统计cat gc.log | grep "Full GC" | awk '{print $1, $NF}' | tail -20# 使用 GCeasy 在线分析工具# 上传 gc.log 到 https://gceasy.io/
◆ 5.1.2 常见问题排查
问题一:Full GC 频繁但内存回收正常
# 诊断命令jstat -gcutil <pid> 1000 10# 观察 OU (Old Generation Utilization)# 如果 Full GC 后 OU 下降明显,说明不是内存泄露,可能是:# 1. 堆内存设置过小# 2. 对象提升速度过快
解决方案:
-
1. 增大堆内存 -Xms8g -Xmx8g # 原来是 4g -
2. 调整新生代比例 -XX:NewRatio=2 # 新生代:老年代 = 1:2 -
3. 调整对象晋升阈值 -XX:MaxTenuringThreshold=15 # 默认15,可适当提高
问题二:Full GC 后内存无法回收
# 诊断命令jstat -gccause <pid> 1000# 如果 Full GC 后 OU 持续 > 80%,确认内存泄露# 立即生成堆转储jcmd <pid> GC.heap_dump /tmp/leak_$(date +%Y%m%d_%H%M%S).hprof
解决方案:按照本文 MAT 分析流程定位泄露点
问题三:应用启动后立即 OOM
-
• 症状:应用刚启动就报 OutOfMemoryError -
• 排查: # 查看启动时加载的类数量jmap -histo <pid> | head -20# 检查是否有超大对象jmap -heap <pid> -
• 解决: -
• 检查是否一次性加载了大量数据(如预热缓存) -
• 分批加载或使用懒加载 -
• 增大堆内存或减少初始化数据量
◆ 5.1.3 调试模式
# 开启详细 GC 日志java -XX:+PrintGCDetails \ -XX:+PrintGCDateStamps \ -XX:+PrintGCApplicationStoppedTime \ -XX:+PrintTenuringDistribution \ -XX:+PrintHeapAtGC \ -Xloggc:gc.log \ -jar app.jar# 使用 JFR (Java Flight Recorder) 进行持续监控java -XX:StartFlightRecording=duration=60s,filename=recording.jfr \ -jar app.jar# 分析 JFR 记录jfr print --events jdk.ObjectAllocationSample recording.jfr
5.2 性能监控
◆ 5.2.1 关键指标监控
# 实时监控脚本#!/bin/bashPID=$(pgrep -f your-app)whiletrue; doecho"===== $(date) ====="# GC 统计 jstat -gcutil $PID 1000 1# 堆内存使用 jcmd $PID VM.native_memory summary | grep "Java Heap"# 线程数 jstack $PID | grep "java.lang.Thread.State" | wc -l# 加载的类数量 jstat -class $PID | tail -1sleep 60done
◆ 5.2.2 监控指标说明
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
◆ 5.2.3 Prometheus 监控配置
# JMX Exporter 配置示例# jmx_exporter_config.ymllowercaseOutputName:truelowercaseOutputLabelNames:truerules:# Heap Memory-pattern:'java.lang<type=Memory><HeapMemoryUsage>(\w+)'name:jvm_heap_memory_$1type:GAUGE# GC Count-pattern:'java.lang<type=GarbageCollector, name=(\w+)><>CollectionCount'name:jvm_gc_collection_countlabels:gc:"$1"type:COUNTER# GC Time-pattern:'java.lang<type=GarbageCollector, name=(\w+)><>CollectionTime'name:jvm_gc_collection_time_mslabels:gc:"$1"type:COUNTER# Grafana 告警规则alert:HighHeapMemoryUsageexpr:(jvm_heap_memory_used/jvm_heap_memory_max)>0.85for:5mlabels:severity:warningannotations:summary:"堆内存使用率过高"description:"{{ $labels.instance }} 堆内存使用率 {{ $value }}%"alert:FrequentFullGCexpr:rate(jvm_gc_collection_count{gc="PSMarkSweep"}[5m])>0.2for:5mlabels:severity:criticalannotations:summary:"Full GC 频繁"description:"{{ $labels.instance }} 5分钟内 Full GC {{ $value }} 次"
5.3 备份与恢复
◆ 5.3.1 自动备份脚本
#!/bin/bash# 定期备份堆转储文件SOURCE_DIR="/var/log/java"BACKUP_DIR="/backup/heap_dumps"RETENTION_DAYS=30# 创建备份目录mkdir -p "$BACKUP_DIR"# 压缩并备份最新的堆转储find "$SOURCE_DIR" -name "*.hprof" -mtime -1 | whileread hprof_file; do filename=$(basename"$hprof_file") tar -czf "$BACKUP_DIR/${filename}.tar.gz""$hprof_file"echo"[$(date)] 已备份: $filename"done# 清理过期备份find "$BACKUP_DIR" -name "*.tar.gz" -mtime +$RETENTION_DAYS -delete# 上传到对象存储(可选)# aws s3 sync "$BACKUP_DIR" s3://your-bucket/heap-dumps/
◆ 5.3.2 问题重现环境准备
-
1. 保存现场: # 生成堆转储jcmd $PID GC.heap_dump heap.hprof# 保存线程栈jstack $PID > thread_dump.txt# 保存 JVM 参数jcmd $PID VM.flags > jvm_flags.txt# 打包环境信息tar -czf diagnostic_$(date +%Y%m%d_%H%M%S).tar.gz \ heap.hprof thread_dump.txt jvm_flags.txt gc.log -
2. 本地分析: # 解压诊断包tar -xzf diagnostic_20250126_143022.tar.gz# 使用 MAT 分析mat heap.hprof -
3. 问题复现: # 使用相同的 JVM 参数启动java $(cat jvm_flags.txt) -jar app.jar
六、总结
6.1 技术要点回顾
-
• ✅ 要点一:MAT 是分析 JVM 内存泄露的最强工具,通过 Leak Suspects Report、Dominator Tree、Histogram 等视图快速定位问题 -
• ✅ 要点二:常见内存泄露场景包括 ThreadLocal 未清理、静态集合缓存、事件监听器未注销、数据库连接未关闭等 -
• ✅ 要点三:生产环境应配置 -XX:+HeapDumpOnOutOfMemoryError自动生成堆转储,并建立完善的监控告警机制 -
• ✅ 要点四:结合 GC 日志、JFR、监控指标等多维度分析,准确判断是否存在内存泄露
6.2 进阶学习方向
-
1. JVM 调优深入: -
• 学习资源:Oracle JVM Tuning Guide -
• 实践建议:在测试环境尝试不同 GC 算法(G1、ZGC、Shenandoah),对比效果 -
2. 分布式系统内存问题排查: -
• 学习资源:Kubernetes Memory Management -
• 实践建议:使用 Arthas 进行在线诊断,无需重启应用 -
3. Native Memory Leak 排查: -
• 学习资源:Native Memory Tracking -
• 实践建议:使用 -XX:NativeMemoryTracking=detail追踪堆外内存
6.3 参考资料
-
• Eclipse MAT 官方文档 – 完整功能说明和示例 -
• 美团技术团队:Java内存泄漏分析系列 – 实战案例丰富 -
• Alibaba Arthas – 在线诊断神器 -
• GCeasy 在线分析 – GC 日志可视化分析
附录
A. 命令速查表
# 生成堆转储jmap -dump:live,format=b,file=heap.hprof <pid> # jmap方式jcmd <pid> GC.heap_dump heap.hprof # jcmd方式(推荐)# GC 统计jstat -gcutil <pid> 1000 10 # 每秒输出GC统计,共10次jstat -gccause <pid> # 查看GC原因# 线程分析jstack <pid> > threads.txt # 导出线程栈jstack <pid> | grep "java.lang.Thread.State" | wc -l # 统计线程数# 堆信息jcmd <pid> VM.heap_summary # 查看堆摘要jmap -heap <pid> # 查看堆详细配置# 类统计jmap -histo:live <pid> | head -30 # 查看存活对象统计# Native Memoryjcmd <pid> VM.native_memory summary # 查看本地内存使用
B. MAT 快捷键
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
C. 术语表
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
夜雨聆风