Java内存泄漏排查需四步闭环:先定性(阶梯式内存上涨+Full GC后老年代>85%不回落),再抓现场(GC日志、Heap Dump、jstat快照),接着用MAT分析Leak Suspects和Path to GC Roots定位强引用源头,最后分层修复(限容/过期策略+迁外部缓存+try-with-resources+告警基线)。

Java内存泄漏排查不是靠猜,而是按节奏走:从现象确认、数据采集、工具分析到代码修复,每一步都得踩准。规范操作的核心是“不跳步、不绕弯、不凭经验拍脑袋”。下面分四个关键环节说清楚怎么做。
一、先定性:确认真是泄漏,不是误判
别一看到OOM就开干。先用监控和日志交叉验证,满足以下两点才能进入排查流程:
- 堆内存使用曲线呈阶梯式上升,Full GC后老年代占用率仍高于85%,且无法回落
- 服务重启后,相同业务量下,内存增长周期明显缩短(比如上次撑5天,这次2天就OOM)
注意区分:GC频繁但内存能回落,大概率是业务峰值;内存只涨不跌、越GC越卡,才是泄漏信号。
二、抓现场:精准采集三类关键数据
线上环境不能停服,必须一次到位拿到有效证据:
Java开发手册规约集合,基于阿里巴巴Java开发手册(嵩山版)。 涵盖7大维度:编程规约、异常日志、单元测试、安全规约、MySQL数据库、工程结构、设计规约。 当用户需要:(1) 编写或审查Java代码 (2) 检查命名/代码规范 (3) 处理异常和日志 (4) 编写单元测试 (5) 安全编码 (6) 数据库设...
立即学习“Java免费学习笔记(深入)”;
-
GC日志:JVM启动加参数
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/path/gc.log,重点看Full GC次数、耗时、前后老年代占比 -
堆转储文件(Heap Dump):OOM时自动生成最可靠,加
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/dump/;若未触发OOM但内存持续涨,可用jmap -dump:format=b,file=heap.hprof <pid>手动抓取 -
实时内存快照:用
jstat -gcutil <pid> 1000 10每秒采样10次,确认Eden/S0/S1/Old/Metaspace各区域变化趋势
三、用工具定位泄漏对象
别在hprof文件里手动翻——用MAT(Eclipse Memory Analyzer)直接聚焦问题:
- 打开hprof后,点“Leak Suspects Report”,优先看Top 1~2个嫌疑对象,通常占堆70%以上
- 进“Dominator Tree”,按“Retained Heap”倒序排列,找实例数异常多、单个Retained Heap大的类
- 右键可疑对象 → “Path to GC Roots” → 勾选“exclude weak/soft references”,看谁在强引用它(常见源头:static字段、ThreadLocal、监听器注册表)
如果发现是某个缓存Map占满堆,再结合代码确认:是不是静态Map?有没有清理逻辑?key是否用了String以外的类型导致无法remove?
四、改代码+防复发:不止修bug,还要建防线
修复不是删掉一行代码就完事,要分层堵漏:
-
立即止血:给静态缓存加容量限制(如Caffeine的
maximumSize(1000))、加过期策略(expireAfterWrite(30, MINUTES))、补全remove调用 -
长期加固:把本地静态缓存迁移到Redis等外部存储;对所有ThreadLocal变量,确保在请求结束时调用
remove();数据库连接、流操作统一用try-with-resources - 上线防护:加内存告警(老年代>80%触发)、定期导出堆快照做基线比对、CI阶段接入SpotBugs检查未关闭资源和静态集合滥用

















