连载中 14/20

OOM 地图:七种 OutOfMemoryError 各从哪里来

2026-09-17 · 4 阅读 · 0 评论 · 0 赞

不是所有 OOM 都叫堆满

"OOM"在 Java 里是个家族名,不指某一具体报错。看 OOM 报错要像看医院分诊——不同出生地、不同急救方案。先看错误信息最后一行的关键词:Java heap space / Metaspace / Direct buffer / GC overhead / unable to create native thread / Requested array size / Slow worker,七种各归一位。

堆内存:最常见的一种

java.lang.OutOfMemoryError: Java heap space:堆里放不下新对象了。原因有三类——泄漏(对象被拴住不释放,堆越涨越高)、瞬时流量(一次查询结果太大,比如把百万行全捞进内存)、配置不足(-Xmx 太小)。前两类查代码,第三类调参数。另一种 heap OOM 是 GC overhead limit exceeded——堆没满,但 GC 花了 98% 时间只收回 2% 内存,JVM 认为继续收是浪费生命直接罢工,本质是泄漏的早期信号。

非堆 OOM 三兄弟

Metaspace:类元信息放不下,常见于动态生成类的场景(CGLIB、字节码增强)。JDK 8 之前在永久代(PermGen),之后搬进本地内存。-XX:MaxMetaspaceSize 给个上限更稳,不设的话理论上能吃到物理内存上限。泄漏特征是 MAT 里 Class 对象一大堆、同名重复。Direct buffer memory:直接内存(堆外)耗尽——NIO、Netty 用的多。堆转储看不出问题(堆外不在 dump 里),得靠 -XX:MaxDirectMemorySize 和 jcmd PID VM.native_memory 查(需开 -XX:NativeMemoryTracking=summary)。unable to create new native thread:线程数到系统上限了,不是 JVM 内存问题——查 ulimit -u 和进程线程数,往往是创建线程过多或 fork bomb 限制。

栈 OOM 两兄弟

// StackOverflowError:栈深 -Xss 打穿
void recurse() { recurse(); }   // 栈帧无限叠加
// 通常是个 bug,排查是找到递归出口;栈深太浅可加大 -Xss

// OutOfMemoryError: unable to create new native thread
new Thread(() -> { while(true); }).start();   // 每个线程占 1M 栈
// 线程数 × 栈深 > 进程可用内存,就报这个错
//   线程池上限别瞎设,配合 -Xss 一起算

这两个区别要清楚:StackOverflowError 是"一条栈压穿",OutOfMemoryError unable to create 是"开不出新栈"——前者是单线程栈深问题,后者是线程数问题。还有个 Requested array size exceeds VM limit:分配超大数组(new byte[Integer.MAX_VALUE]),改代码即可,不是内存问题。

一张地图收尾

报错关键词出生地首查
Java heap space堆堆转储 + MAT
GC overhead limit堆GC 日志,泄漏早期
Metaspace本地内存动态类生成
Direct buffer memory直接内存NMT、Netty 检查
unable to create native thread线程/系统线程数 + ulimit

下一篇换个角度:不是内存出问题,是 CPU 出问题——"应用 CPU 100%"的排查流程。

☕
503

10 年全栈工程师 · 503咖啡馆主理人

#OutOfMemoryError#Metaspace#DirectBuffer#GC overhead#线程数限制

评论 (0)

热门推荐

连载中 11/22

主从搭建实操:从零配出一主两从

光讲原理不过瘾?手把手搭一主两从:my.cnf 六个参数、复制账号、GTID、CHANGE REPLICATION SOURCE TO、SHOW REPLICA STATUS 验收,附翻车排查清单。

#MySQL#主从复制#GTID#主从搭建#高可用
2026-05-07 · 10101 阅读 · 0 评论 · 0 赞
连载中 16/22

连接池:HikariCP 参数与连接风暴

连接池不是越大越好:8 核机器配 1000 连接反而更慢的数学原理,HikariCP 四个必调参数,maxLifetime 与 wait_timeout 的隐形陷阱。

#MySQL#连接池#HikariCP#maxLifetime#连接风暴
2026-05-10 · 9873 阅读 · 0 评论 · 0 赞
连载中 4/16

缓存穿透:恶意 ID 打穿 MySQL 的四道防线

请求的数据在缓存和数据库里都不存在时,缓存形同虚设。聊聊参数校验、空值缓存、布隆过滤器、限流熔断四道防线的原理与组合打法。

#Redis#缓存穿透#布隆过滤器#高可用
2026-05-16 · 9294 阅读 · 21 评论 · 287 赞