JVM内存泄漏排查需按“确认泄漏→锁定膨胀对象→追溯代码源头”三步进行,使用jstat、jmap、MAT等工具组合分析,全程无需重启或改代码。

JVM 内存泄漏排查不靠猜,靠证据链:先确认泄漏是否正在发生,再锁定膨胀对象,最后追溯到代码源头。整个过程依赖标准命令与分析工具的协同,无需重启、不改代码,5 分钟内就能完成初步定位。
用 jstat 快速确认泄漏迹象
这是第一响应工具,开销极低且不停顿应用。执行 jstat -gcutil <pid> 1000(每秒刷新),重点关注三列:
- O 列(Old Gen 使用率):持续上升且不回落,比如从 45% 涨到 92% 并卡住,说明老年代对象无法回收
- FGC 列(Full GC 次数):短时间内猛增(如 90 秒内从 0 到 12),但每次回收后 O 值几乎不变
- FGCT 列(Full GC 总耗时):数值不断变长,同时 YGC 频次也升高,表明新生代对象过早晋升
只要这三项同时异常,基本可判定存在活跃的内存泄漏。
用 jmap 定位膨胀类
确认泄漏后,立即执行 jmap -histo:live <pid> | head -n 20。它只统计当前可达对象,排除已死亡干扰:
- 关注 #instances 和 #bytes 都排前几的类,尤其是业务包路径下的类(如 com.example.service.UserCache、org.apache.http.impl.client.CloseableHttpClient)
- 注意常见数组类型:[C(char[],多为字符串)、[[I(int[][],可能是缓存结构)、[Ljava.lang.Object;(Object 数组,常见于集合扩容)
- 若某类实例数每分钟增长数千,而业务请求量无对应变化,大概率就是泄漏源头
导出堆快照并用 MAT 深度分析
定位到可疑类后,导出轻量级存活堆转储:jmap -dump:live,format=b,file=leak-$(date +%s).hprof <pid>
- 加 live 参数确保只导出可达对象,文件更小、分析更快
- 命名带时间戳,方便后续比对多个快照的变化趋势
- 用 MAT 打开 → 运行 “Leak Suspects Report” → 查看 Dominator Tree 中占比最高的对象链
- 重点看 “Path to GC Roots”,找强引用路径:static 字段、ThreadLocal、未注销监听器、未关闭的流或连接
结合配置与日志交叉验证
运行 jinfo -flags <pid> 检查关键参数:
- 是否启用 -XX:+HeapDumpOnOutOfMemoryError?没开就补上,下次 OOM 自动留证据
- 堆大小是否合理?-Xms 和 -Xmx 差距过大,或 -XX:NewRatio 设置过小(如设为 1),可能掩盖泄漏表象
- 元空间是否异常?用 jstat -gcmetacapacity <pid> 查看 M 区使用率,长期 >90% 可能是类加载泄漏
- 开启 GC 日志(-Xlog:gc*:file=gc.log:time,uptime,pid,tags,level),用 GCViewer 或 GCEasy 分析 Full GC 频次、回收效果及老年代增长趋势

















