JVM 堆内存溢出后,其他线程是否可继续工作?(美团)¶
结论速答¶
可以继续工作,但很不稳定,最终通常会整体崩溃。 准确说:单个线程抛 OutOfMemoryError: Java heap space 不会立刻让 JVM 退出,其他线程还能跑;但只要还有线程持续分配对象,新的 OOM 会不断抛出,最终 JVM 无法恢复。
详细分析¶
1. OOM 是如何抛出的¶
当线程尝试在堆上分配对象,GC 后仍无足够内存,JVM 在该线程的上下文中抛出 OutOfMemoryError。这意味着:
- 抛 OOM 的线程:捕获或不捕获,都由它自己决定后续行为。
- 其他线程:不受直接影响,它们的线程栈、寄存器状态完好。
- 堆本身:因为 GC 已经尽量回收,大部分情况下堆已经接近满。
2. 为什么"还能工作"¶
OOM 是一个 Error,不是 JVM exit。JVM 进程还在运行,只要:
- 其他线程不继续分配大对象。
- 已有对象引用正常。
- GC 能回收少量内存。
那么其他线程的方法调用、字符串拼接(小对象)、日志写入仍可能继续。
3. 为什么"最终会崩"¶
- 只要还有请求进来,应用就要创建新对象(DTO、字符串、集合)。
- 每次分配都可能触发 Full GC,GC 时间越来越长。
- 一旦再次分配失败,又一个 OOM 被抛出。
- 如果 OOM 发生在核心线程(如 Tomcat 工作线程)且未捕获,请求失败;如果未捕获的 OOM 出现在非守护线程,JVM 会退出。
4. 常见 OOM 类型对比¶
| OOM | 含义 | 其他线程是否能工作 |
|---|---|---|
Java heap space |
堆爆 | 通常短暂可工作,很快再爆 |
GC overhead limit exceeded |
GC 花 98% 时间回收不到 2% | 同上 |
Metaspace |
元空间爆 | 类加载无法继续,可能影响新功能 |
unable to create new native thread |
OS 无法建线程 | 已有线程正常,新请求失败 |
Direct buffer memory |
堆外内存爆 | 直接受影响 |
实战建议¶
- 不要 catch OOM 后假装没事。OOM 说明系统状态已异常,应快速失败、告警、重启。
- 线上配置: 自动 dump 用于事后分析。
- 容器环境配合 livenessProbe,OOM 后自动重启 Pod。
美团这道题的考察点
不是考"能不能继续",而是考你是否理解 OOM 是异常不是致命信号,以及线上如何兜底。答出"快速失败 + dump + 自动重启"才是完整答案。