Java内存泄漏排查核心是确认“对象该回收却未回收”,分三步:看现象(老年代长期>90%、Full GC后回收<20%、响应变慢与GC重合)、抓快照(jmap -dump:live或-XX:+HeapDumpOnOutOfMemoryError)、用MAT查Leak Suspects和Path to GC Roots,重点审查静态集合、ThreadLocal、资源未关闭、内部类四类陷阱。

发现与排查 Java 内存泄漏,核心是抓住“对象该回收却没被回收”这一本质。不靠猜,也不靠加内存,而是用现象判断 + 工具定位 + 代码审查三步联动,快速锁定问题源头。
看现象:先确认是不是泄漏
内存持续上涨不等于泄漏,关键看 GC 后是否回落:
- 监控显示老年代使用率长期 >90%,且 Full GC 后回收比例低于 20%
- GC 频率明显加快(如从每小时 1 次变成每分钟多次),单次耗时超 1 秒
- 服务响应变慢、接口超时增多,且与 GC 时间点高度重合
- OOM 报错反复出现,重启后几小时内复现,不是偶发高峰
抓快照:用 jmap 和 JVM 参数留证据
早留堆快照,胜过事后大海捞针:
- 启动时加参数:-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/dump/ -XX:+PrintGCDetails -Xloggc:/data/logs/gc.log
- 运行中怀疑泄漏时,立即执行:jmap -dump:live,format=b,file=heap.hprof <pid>
- 注意:dump 文件大小≈堆内存上限,分析机内存需大于该文件
查工具:MAT 或 VisualVM 定位大对象和引用链
堆快照不是用来“看大小”,而是找“谁在拦着 GC”:
立即学习“Java免费学习笔记(深入)”;
- 用 MAT 打开 .hprof 文件,打开 Leak Suspects Report,直接看可疑泄漏点
- 重点看 dominator tree,找占用堆最大的类及其实例数
- 对高嫌疑对象右键 → Path to GC Roots → exclude weak/soft references,查看强引用链
- 常见拦截路径:静态集合 → ThreadLocal → 内部类隐式引用 → 监听器未注销
审代码:盯死四类高频陷阱
工具只能告诉你“谁没被回收”,代码审查才能回答“为什么没被回收”:
-
静态持有:检查
public static Map、static List等,确认是否有清理逻辑(定时清除、LRU 淘汰、容量限制) -
ThreadLocal 泄漏:所有
ThreadLocal.set()后,必须配tl.remove()(不只是set(null)),尤其在线程池场景 -
资源未关闭:所有
InputStream、Connection、ResultSet必须用 try-with-resources,避免 finally 中裸写 close() -
内部类滥用:非静态内部类或匿名类(如
new Runnable(){...})传给线程、监听器、异步回调时,会持外部类引用——优先改用静态内部类 + 显式传参


















