OOM发生在线程申请内存资源时,申请不到而抛出来的异常。

当堆内存接近爆掉时,一般导致堆内存爆满的那个线程,是最大概率会被jvm干掉的。但这个事情无绝对,还有较小的概率是其它线程此刻刚好去申请内存,申请不到就会抛出OOM,被误干掉。

因此内存爆满时,会导致线程OOM具有不确定性。这个危害将更具隐蔽性。

如果要避免这种不确定性,也可以设置java在发生OOM时直接退出程序:

-XX:+ExitOnOutOfMemoryError -XX:+CrashOnOutOfMemoryError

上面这个配置java默认是不开启的,其实这也是个两难的选择,我们也不希望出现任何oom整个java进程就退出了,不过在分布式环境下,这种直接退出的做法还是一定程度可以接受的。

案例

最近生产有一个应用,它会跑一个比较长且很占内存的定时任务,每半小时跑一次。由于这个定时任务数据量比较大,还有可能两个任务同时跑(理论上有这种可能),此时如果内存开得比较小的话,这个定时任务就会占大量内存,也出现过这个任务线程OOM的情况。

但是同时我们发现,当这个任务占了大量内存后,GC活动的CPU占比会持续100%一段时间,然后所有url就访问不了了,即便定时任务完成后恢复正常了,url还是访问不了,而定时任务后续又正常在跑了。

排查到最后的情况是,tomcat的Acceptor线程挂了,难怪url会访问不了。Acceptor之所以挂了,是在某次它执行的时候,申请内存申请不到而发生OOM,导致它退出的,而它退出后不像线程池之类的能自行再创建一个,就等于永久退出没法自行修复了。

这个案例我们可以看到OOM危害的随机性了,任何线程都可能因为当前内存被耗尽而产生OOM,进而使得线程的结束具有不确定性。而一些关键线程的结束,必然会使得整个java进程的功能不完整。

文档更新时间: 2020-08-22 09:13   作者:nick