直接看异常堆栈末尾的类和行号,再结合堆转储分析对象引用链,就能快速锁定问题代码段;oom后缀明确指示问题区域,配合jvm自动dump和mat分析可精准定位内存泄漏根因。

直接看异常堆栈末尾的类和行号,再结合堆转储分析对象引用链,就能快速锁定问题代码段。别一上来就调大堆内存,那只是掩盖问题。
看OOM错误后缀,先圈定内存区域
错误信息末尾明确提示问题区域:
- Java heap space:堆里对象太多或泄漏,重点查业务逻辑中集合、缓存、大对象创建
- Metaspace:类加载过多,查反射、动态代理(如Spring AOP、MyBatis)、热部署场景
-
Direct buffer memory:NIO直接内存爆了,查
ByteBuffer.allocateDirect()调用点,尤其未配合cleaner或free()释放的地方 - unable to create new native thread:不是内存不够,是线程数超限,查线程池配置、ThreadLocal未清理、递归创建线程等
- GC overhead limit exceeded:GC几乎失效,98%时间在回收却只腾出不到2%空间——大概率是老年代有大量长期存活对象,典型内存泄漏前兆
用JVM参数自动抓现场
上线前务必加这两项,出问题时能自动生成关键证据:
-
-XX:+HeapDumpOnOutOfMemoryError:OOM发生时立刻生成.hprof文件 -
-XX:HeapDumpPath=/opt/logs/oom/:指定dump存放路径,确保目录可写且有足够空间 - (可选)
-XX:+PrintGCDetails -Xloggc:/opt/logs/gc.log:记录GC行为,辅助判断是否频繁Full GC、回收效果差
有了dump文件,问题就从“猜”变成“查”。没这个,所有分析都是拍脑袋。
用MAT直击内存占用源头
把.hprof拖进Eclipse MAT,三步定位业务代码:
- 打开Dominator Tree:看Retained Heap最大的几个对象,通常排第一的就是泄漏根或主容器(比如静态Map、缓存List)
- 右键该对象 → Path to GC Roots(排除弱/软引用):显示谁一直强引用着它,链条末端往往就是你的业务类和方法
- 对照Histogram里数量异常多的类(比如几百万个
OrderDTO),再查其构造位置——常出现在循环内new、缓存未设上限、流式读取未分页等场景
结合日志和代码快速验证
工具给出线索后,回到代码确认:
- 静态集合(
static Map, ?>、static List>)是否无限add?有没有清理机制? - 缓存是否用了Guava/Caffeine但没配size limit或expireAfterWrite?
- 数据库查询是否一次性load全表?有没有
limit或游标分页? - 文件/网络流是否用了
InputStream、Connection却没在finally或try-with-resources里close? - ThreadLocal变量是否在请求结束时调用
remove()?尤其在Web应用Filter或Interceptor中
找到对应代码段,加日志打点验证:比如在疑似泄漏的add操作前后打印集合size,运行一段时间观察是否持续增长。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











