java程序长期运行oom主因是堆内存泄漏或资源未释放,需通过jvm参数自动转储堆快照,用mat分析对象引用链,结合jstack定位线程级资源持有问题。

确认具体OOM类型,锁定排查方向
先看日志里的完整错误信息,它决定了后续工具和策略:
- java.lang.OutOfMemoryError: Java heap space → 主攻堆内存泄漏,查对象堆积
- java.lang.OutOfMemoryError: Metaspace → 查类加载器泄漏、动态代理/脚本生成过多
- java.lang.OutOfMemoryError: Unable to create new native thread → 查线程数是否失控(如线程池未复用、异步任务未限流)
- java.lang.OutOfMemoryError: GC overhead limit exceeded → 表明GC已疲于奔命,大概率存在强引用泄漏
启用自动堆转储,保留现场证据
运行时加JVM参数,确保OOM发生时能拿到关键快照:
-XX:+HeapDumpOnOutOfMemoryError-
-XX:HeapDumpPath=/data/dumps/(确保路径有写权限) - 可选:
-XX:+PrintGCDetails -Xloggc:/data/logs/gc.log,辅助判断GC行为
注意:不要等OOM再收集——如果服务允许,也可主动触发:jmap -dump:format=b,file=/tmp/heap.hprof <pid></pid>,对比多个时间点的dump更易发现增长趋势。
用MAT分析堆转储,定位泄漏根因
Eclipse Memory Analyzer(MAT)是最高效的开源工具,关键操作聚焦三点:
- 打开dump后,直接看Leak Suspects Report——它会自动标出最可疑的几组对象及保留集大小
- 打开Dominator Tree,按“Retained Heap”降序排列,找占用最大的类(如
HashMap、ArrayList、自定义缓存类) - 对可疑对象右键 → Path to GC Roots(排除弱/软引用),重点看Strong Reference链:谁长期持有着它?是不是静态Map没清理?是不是监听器未反注册?是不是ThreadLocal变量未remove?
常见泄漏模式举例:Spring Bean中static Map缓存用户会话;WebSocket连接关闭后未清空关联的业务对象;数据库连接池配置不当导致Connection对象堆积。
结合线程堆栈,验证资源生命周期异常
堆溢出常伴随线程问题,比如线程池阻塞、IO等待未超时,导致对象被线程上下文长期引用:
- 用
jstack <pid> > thread.log</pid>抓当前所有线程状态 - 重点关注:
WAITING(尤其是阻塞在锁或队列)、RUNNABLE但CPU不高(可能卡在IO)、大量java.util.concurrent.ThreadPoolExecutor$Worker线程 - 将thread.log中活跃线程的堆栈,与MAT里“Path to GC Roots”中出现的线程名交叉比对,确认是否某类任务执行后未释放资源
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











