oom需分三类定位:堆溢出查老年代增长与大对象;元空间溢出查动态类加载;直接内存溢出查direct buffer使用。关键在监控、dump分析和代码审计,而非盲目调参。

Java堆内存溢出(java.lang.OutOfMemoryError: Java heap space)怎么确认和解决
这是最常遇到的OOM,根本原因是对象太多或单个对象太大,超出了-Xmx设定的堆上限。别急着加内存——先看是不是真有泄漏或不合理缓存。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 用
jstat -gc <pid></pid>观察OU(老年代使用量)是否持续上涨且GC后不回落 - 触发一次
jmap -dump:format=b,file=heap.hprof <pid></pid>,用VisualVM或Eclipse MAT打开,按“Retained Heap”排序,重点看前几项是不是业务类(比如HashMap、ArrayList、自定义缓存容器) - 检查是否有未关闭的流、监听器、静态集合引用了Activity/Context(Android)、或Spring中
@Scope("singleton")却持有请求级对象 -
-XX:+HeapDumpOnOutOfMemoryError必须加在启动参数里,否则OOM发生时dump就丢了
元空间溢出(java.lang.OutOfMemoryError: Metaspace)不是类太多就是动态生成太猛
JDK 8+ 之后永久代被元空间取代,它直接使用本地内存,默认无上限(只受系统限制),所以溢出往往意味着类加载失控,而不是配置小了。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 查
jstat -gcmetacapacity <pid></pid>中的MC(元空间容量)和MU(已使用),如果MU逼近MC且持续增长,说明类在不断加载没卸载 - 常见场景:大量使用
cglib、javassist、ByteBuddy生成代理类(如Spring AOP默认用cglib);热部署框架(如DevTools)反复重载类;OSGi或模块化容器中Bundle频繁启停 - 临时缓解可加
-XX:MaxMetaspaceSize=256m,但治标不治本;关键要定位哪个ClassLoader加载了异常多的类——用jcmd <pid> VM.native_memory summary scale=MB</pid>或jmap -cl <pid></pid>看类加载器分布 - 注意
-XX:MetaspaceSize是初始触发GC的阈值,不是上限;设太小会导致频繁元空间GC,反而拖慢启动
直接内存溢出(java.lang.OutOfMemoryError: Direct buffer memory)跟堆参数无关,但常被误判
这是ByteBuffer.allocateDirect()申请的堆外内存超限,受-XX:MaxDirectMemorySize控制(默认等于-Xmx)。哪怕堆还空着,它也能单独爆掉。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 确认是否用了Netty、NIO框架、或自己调了
allocateDirect();特别注意Netty的PooledByteBufAllocator如果没正确release(),会累积导致OOM - 用
jcmd <pid> VM.native_memory summary scale=MB</pid>查看Direct段占用,比堆dump更直接 - 加JVM参数
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps,如果看到Direct buffer memory相关GC日志(如G1 Evacuation Pause里提到Direct),说明回收机制已在干预 - 别用
System.gc()试图强制回收直接内存——它不保证触发Cleaner,且会干扰正常GC节奏
其他变体:无法创建新线程、GC overhead limit exceeded、Compressed class space
这些不是独立内存区域问题,而是连锁反应或配置失衡的结果。
实操建议:
立即学习“Java免费学习笔记(深入)”;
-
java.lang.OutOfMemoryError: unable to create new native thread:通常是操作系统级线程数到上限(ulimit -u),不是JVM内存不够;检查是否线程池无界、短生命周期线程滥用new Thread()、或-Xss设得过大挤占了栈空间 -
GC overhead limit exceeded:表示98%时间在GC,但只回收不到2%堆内存,本质是堆太小或对象存活率太高;优先看GC日志里的PSYoungGen/G1 Old Gen回收前后占比,而不是直接调大堆 -
Compressed class space溢出:仅出现在开启压缩指针(默认开启)且元空间里类元数据过多时;加-XX:CompressedClassSpaceSize=512m可缓解,但大概率说明你加载了太多框架或重复jar包
真正麻烦的从来不是错误信息本身,而是它背后那个没被释放的ConcurrentHashMap、那个忘了close()的FileChannel、或者那个以为“反正有GC”的静态ThreadLocal<byte></byte>。查OOM,一半功夫在复现前的日志和监控,另一半在怀疑自己写的那行“应该没问题”的代码。










