java内存泄漏排查需按现象锁定范围、用工具定位对象、靠代码确认根源:先通过老年代持续攀升等信号确认泄漏,再用jstat观察gc行为,接着用mat分析堆转储找强引用路径,最后结合静态集合、threadlocal等常见模式修复并压测验证。

Java内存泄漏排查不是靠猜,而是按现象锁定范围、用工具定位对象、靠代码确认根源。核心思路是:先看运行时表现是否符合泄漏特征,再抓现场数据(堆快照/GC日志),最后结合引用链和业务逻辑判断谁“攥着不该攥的引用”。
一、先确认是不是内存泄漏,不是OOM就万事大吉
很多问题被误判为泄漏,其实是配置不足或瞬时压力。真正泄漏有三个典型信号:
- 老年代使用率持续攀升,Full GC后回收极少,且每次GC后内存基线比上次更高
- GC overhead limit exceeded 频繁出现,JVM把大部分时间花在GC上却收不回多少内存
- 服务重启后几分钟内内存又快速上涨,且增长趋势与请求量不成正比
满足其中两条,基本可排除配置问题,进入泄漏排查流程。
二、用jstat盯住GC行为,判断泄漏位置在哪儿
执行 jstat -gcutil
- O列(老年代使用率):长期>85%且缓慢爬升 → 泄漏大概率在老年代对象
- FGC/FGCT列:Full GC次数增多、单次耗时超1秒 → 老年代已严重碎片化或堆积大量存活对象
- YGC/YGCT列:Young GC频繁但Eden回收干净 → 新生代没问题,问题在“活下来”的对象
如果O列稳居95%以上且FGC越来越密,说明对象正在“沉淀”到老年代后无法释放,这是泄漏最典型的运行态表现。
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
三、导出堆转储,用MAT找“不肯放手”的对象
确认异常后,立刻执行:
jmap -dump:live,format=b,file=heap.hprof
用Eclipse MAT打开,重点关注:
- Leak Suspects Report:MAT自动识别的前2个可疑泄漏点,通常直指静态集合或监听器泄漏
- Top Consumers:按 retained heap 排序,看哪些类实例占内存最多
- Path to GC Roots:对可疑大对象右键 → “Show Retained Set” → 再右键 → “Path to GC Roots”(选 exclude weak/soft references)→ 查看谁在强引用它
比如发现10万个User对象没被回收,路径最终指向 com.xxx.Cache.MAP,那基本就是静态缓存没清理。
四、结合代码和场景,验证并修复引用链
工具只能告诉你“谁被谁留着”,真正修复要回到业务逻辑。常见泄漏模式对应检查点:
- 静态集合:搜 static Map/List/Set,确认是否有清理机制或过期策略
- ThreadLocal:检查是否在finally块中调用 remove(),尤其在线程池场景下
- 监听器/回调:注册后是否在销毁时反向注销(如Spring Bean销毁方法、Activity onDestroy)
- 未关闭资源:InputStream/Connection/Channel等是否都在try-with-resources或finally中close
修复后务必压测验证:同样流量下,老年代内存是否平稳,Full GC频率是否回归正常。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










