❝
先说结论:Java应用OOM了,JVM不一定会挂
为什么OOM了,JVM却没挂?
我们经常说“OOM(OutOfMemoryError)一发生,程序就挂了”,但你会发现现实中很多时候:OOM是发生了,可JVM还在运行。这到底是咋回事?
最近无意间获得一份阿里大佬写的刷题笔记,一下子打通了我的任督二脉,进大厂原来没那么难。 这是大佬写的 7701页的BAT大佬写的刷题笔记,让我offer拿到手软
OOM 本质上是个异常
先来看看 OutOfMemoryError 是什么。它其实就是 Java 中的一种异常,准确点说,是 Error 类的一种,属于非检查异常(unchecked exception),不会强制要求你捕获或声明。
它的触发机制很简单:当 JVM 在给某个线程分配内存时,发现堆空间不够用了,就会抛出这个异常。但注意,这个异常是抛给当前线程的,而不是整个 JVM。
Java 的线程模型是“线程独立”:每个线程自己的事自己处理。一个线程就算挂了,也不影响别的线程继续跑。这点在异常处理机制上也体现得很清楚。
OOM 只会终止当前线程
线程的核心执行逻辑在 run() 方法里,这个方法不能声明抛出任何检查异常。
所以,一旦有异常(比如 OOM),要么你手动 try-catch 处理,要么它就会被线程的默认异常处理器接管,最终导致这个线程终止。
但线程终止 ≠ JVM 崩溃。只要还有其他线程(尤其是非守护线程)在跑,JVM 就不会退出。
JVM什么时候才会“挂”?
JVM退出的条件非常明确:所有非守护线程都结束了,JVM 才会退出。
所以,即使某个线程因为 OOM 挂了,只要其他非守护线程还在跑,整个 JVM 就能继续撑下去。这也解释了为啥“发生了 OOM,JVM 却还活着”。
如果你近期准备面试跳槽,建议在cxykk.com在线刷题,涵盖 1万+ 道 Java 面试题,几乎覆盖了所有主流技术面试题、简历模板、算法刷题
举两个例子你就明白了
示例 1:普通线程 OOM,JVM 照样跑
假设你有两个线程,一个在分配内存时发生了 OOM,它挂了,另一个还在正常执行打印日志。那么 JVM 就继续运行,不会退出。
打印内容可能长这样:
我还行...
我还行...
我还行...
说明主线程完全没受到影响。
示例 2:线程池里 OOM,线程还在继续跑
如果你用的是线程池,就更明显了:某个 task 抛了 OOM,当前任务结束了没错,但线程池里的那个线程本身并不会终止,它还能继续接收别的任务。
即使线程池发生了内存溢出,只要主线程还在(而且不是守护线程),JVM 也不会死。
JVM 真正“死”的情况?
那OOM 到底什么时候才会让 JVM 真挂掉?主要有两种情况:
▶ 场景一:主动挂
假设你所有的非守护线程都在不停地申请内存,最终全挂了,那 JVM 就只能跟着退出。这叫主动退出。这时候,JVM 真的已经“油尽灯枯”了,啥都干不了了。
▶ 场景二:系统级别的 OOM Killer 出手
如果你的程序不仅撑爆了 JVM 的内存,还拖累了整个系统,操作系统的 “OOM Killer” 就会上场,把你这个“吃内存大户”干掉。这时候 JVM 是被动下线的。
Linux 内核在系统内存不足时,会评估哪个进程最“坏”(占用最多资源),然后直接杀掉。这叫被动退出。
✅ 总结一句话:
❝
OOM 不等于 JVM 挂了,只有所有非守护线程都挂了或者被操作系统强制干掉时,JVM 才真的退出。
如你所见,这就是一个看似“程序挂了”的误区,其实只是线程挂了,而不是 JVM 真挂了。
最后说一句(求关注,求赞,别白嫖我)
最近无意间获得一份阿里大佬写的刷题笔记,一下子打通了我的任督二脉,进大厂原来没那么难。 这是大佬写的 7701页的BAT大佬写的刷题笔记,让我offer拿到手软
本文,已收录于,我的技术网站 cxykk.com:程序员编程资料站,有大厂完整面经,工作技术,架构师成长之路,等经验分享