Java线上内存持续增长需先确认是否真实泄漏:通过GC日志观察老年代单向爬升、Full GC回收极少、无异常晋升失败;再用jmap/jstack抓快照;MAT分析Leak Suspects和Dominator Tree定位强引用对象;最后回溯static集合、ThreadLocal、监听器未注销等高频泄漏点。

Java 线上服务内存持续增长,不是看“用了多少”,而是看“为什么释放不掉”。关键在区分是真实泄漏,还是配置或使用方式导致的假性增长。定位必须从现象出发,用数据说话,而不是重启、调参、猜代码。
先确认是不是真泄漏
打开 GC 日志(-Xlog:gc*:file=gc.log:time),重点观察三件事:
- 老年代使用率是否单向爬升,且 Full GC 后回收极少(比如只降 1%~2%)
- 年轻代 Minor GC 频率正常、回收充分,但堆总占用仍缓慢上涨
- 是否存在 promotion failure(晋升失败)或 concurrent mode failure(CMS)/ evacuation failure(G1)等异常提示
如果老年代持续逼近上限、Full GC 越来越勤却“清不动”,基本可判定存在对象长期存活或无法回收——这就是泄漏的典型信号。
抓快照:保留现场比重启更关键
服务还在跑但内存已明显偏高时,立刻执行:
-
jmap -dump:live,format=b,file=heap.hprof
:加 live 参数只导出存活对象,避免已死对象干扰分析 -
jstack
> thread.log :查是否有大量 WAITING/TIMED_WAITING 线程堆积(如缓存刷新线程卡住、监听器未注销) - 若已 OOM,确保启动时配了 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/dump/,自动捕获最接近崩溃点的快照
用 MAT 找“钉子户”对象
把 heap.hprof 拖进 Eclipse MAT,核心动作就两个:
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
- 点开 Leak Suspects Report:它会自动标出内存占比最高、引用链可疑的几组对象(比如某个静态 Map 占 60% 堆)
- 切换到 Dominator Tree,按“Retained Heap”倒序,找排在前列的自定义类(非 JDK 内部类),右键 → “Path to GC Roots” → 勾选“exclude weak/soft references”,看谁在强持有它
常见钉子户包括:未清理的 ThreadLocal 变量、静态 ConcurrentHashMap 缓存、EventListener 注册后没反注册、内部类隐式持有了 Activity/Servlet 实例。
回溯代码:关注高频泄漏场景
根据 MAT 定位到的具体类和引用链,重点检查这几类代码:
- 所有 static 集合(Map/List/Set)是否做了容量控制或过期淘汰
- ThreadLocal 是否在 filter 或 finally 块中调用了 remove()
- 监听器、回调、RxJava/Flux 订阅是否在生命周期结束时 unsubscribe/dispose
- 大对象(如 byte[]、StringBuilder、Excel 导出流)是否及时 close 或置 null
不要只看新增代码——很多泄漏是旧逻辑在新流量下暴露出来的,比如原来每小时刷一次缓存,现在改成每秒刷,静态 Map 就撑爆了。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










