Java内存泄漏表现为堆内存持续上涨、Full GC后不回落,排查需分四步:确认泄漏(查老年代使用率、GC耗时、重启复现)、抓取堆快照(jmap或VisualVM)、MAT分析引用链定位根因(static/ThreadLocal/监听器)、修复验证(本地压测、灰度对比、CI监控)。

Java虚拟机内存泄漏不是“突然爆掉”,而是堆内存持续上涨、Full GC后无法回落,最终拖垮服务。排查必须分阶段推进,不能只盯着OOM报错——那只是结果,不是原因。
第一步:确认确实是内存泄漏,不是其他问题
别一看到内存高就喊“泄漏”。先用三个关键指标交叉验证:
- 用 jstat -gc PID 1000 5 观察:老年代(O列)使用率长期>90%,且每次Full GC后下降幅度极小(比如只降1%~2%)
- 看GC日志或监控:出现 GC overhead limit exceeded,或单次Full GC耗时稳定超过1.5秒,频率逐小时加快
- 重启服务后,相同业务压力下,30分钟内堆内存又回到报警水位——这是泄漏最典型的“复现快”特征
如果只是高峰期内存涨、低峰期GC回落,那是正常波动;如果元空间(M列)暴涨并报 OutOfMemoryError: Metaspace,那大概率是类加载器泄漏,不是堆泄漏。
第二步:快速抓取和初筛堆快照
确认泄漏后,立刻导出堆快照,但别盲目 dump 全量:
- 执行 jmap -dump:live,format=b,file=heap_$(date +%s).hprof PID ——加 live 参数可排除已标记但未回收的对象,更准更快
- 用 VisualVM 连上进程 → Monitor 标签页 看实时堆曲线:若呈现阶梯式上升(每次Full GC后不归位),直接点“Heap Dump”按钮,比命令行更省心
- 打开快照后,先看 Classes 视图按“Objects”倒序:找实例数异常多的类(比如几十万甚至上百万个),尤其是你自己写的业务类或缓存类
例如看到 com.example.OrderCacheEntry 实例超50万个,而正常应<5000,这就锁定了重点方向。
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
第三步:深挖引用链,定位根因代码
光知道类多没用,得知道它为什么活下来。这时换 MAT(Eclipse Memory Analyzer)做深度分析:
- 用 MAT 打开 hprof 文件,运行 Leak Suspects Report ——它会自动标出最可疑的几个对象组,并给出保留内存占比
- 点进可疑项,右键 → Path to GC Roots → exclude weak/soft references:这条路径会清晰显示谁在“拽着”这些对象不让GC回收
- 常见路径终点包括:static 字段(如 CacheUtil.cacheMap)、ThreadLocal 变量(尤其在线程池中未清理)、未注销的监听器(如 addListener 后忘了 remove)
比如路径终点是 com.example.config.GlobalConfig.INSTANCE.cacheMap,那就立刻去查这个 Map 是不是只 put 不 remove,或者没配过期策略。
第四步:修复与验证闭环
改完代码不能只等上线,要验证是否真解决:
- 本地复现:用 JMeter 模拟相同请求流量,启动时加 -XX:+PrintGCDetails -Xloggc:gc.log,观察 Full GC 后老年代是否回落到初始水平
- 灰度验证:新版本上线后,盯住 jstat -gc PID 5000 的 O 列变化趋势,对比旧版本同一时段数据
- 长效防护:在 CI 流程中加入 Arthas watch 命令,对高危类(如含 static 集合的类)做方法调用监控,发现异常增长自动告警
修复不等于删代码——静态缓存可用 WeakHashMap 或 Caffeine.newBuilder().maximumSize(1000).expireAfterWrite(10, TimeUnit.MINUTES) 替代;资源类务必用 try-with-resources 或 finally 显式 close。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










