OOM导致JVM退出?

static class OOMObject {
}
 
// 为快速发生oom,设置堆大小; VM args: -Xms20m -Xmx20m 
public static void main(String[] args) throws InterruptedException {
     new Thread(() -> {
         List<OOMObject> list = new ArrayList<>();
         while (true) {
             list.add(new OOMObject());
         }
     }).start();
 
     while (true) {
         System.out.println(Thread.currentThread().getName() + " continuing...");
         Thread.sleep(1000L);
     }
 }

线程抛出java.lang.OutOfMemoryError: Java heap space后,main线程依旧会循环打印main continuing…。
线程中发生OOM异常如此,发生其他异常也如此,不影响其他线程,也不会导致JVM退出。

OOM与JVM退出

OOM的发生表示了此刻JVM堆内存告罄,不能分配出更多的资源,或者gc回收效率不可观。一个线程的OOM,在一定程度的并发下,若此时其他线程也需要申请堆内存,那么其他线程也会因为申请不到内存而OOM,甚至连锁反应导致整个JVM的退出。

以上示例没有导致JVM退出的原因在于,线程通过往局部变量集合中不断加入对象,产生OOM。线程因异常退出后,集合中的对象由于引用不可达,会被gc,这样就有了足够的堆内存供其他线程使用。

若示例中的list是一个“全局”的类static变量,那么即使线程退出,内存也得不到释放。这时其他线程如果不断再申请堆内存资源,就会造成连锁反应导致JVM退出。

一、 OOM几种情况

1、栈溢出。错误信息:stackOverFlower或者OOM信息指向Thread。
如果日志中有这种信息,建议检查方法中是否有死递归
2、堆溢出:
日志指向heap
如果是内存溢出,则通过 调大 -Xms,-Xmx参数。
如果不是内存泄漏,就是说内存中的对象却是都是必须存活的,那么久应该检查JVM的堆参数设置,与机器的内存对比,看是否还有可以调整的空间,再从代码上检查是否存在某些对象生命周期过长、持有状态时间过长、存储结构设计不合理等情况,尽量减少程序运行时的内存消耗。
3、方法区溢出
日志OOM后又指向MetaSpace.
原因
(1) 运行时常量池溢出
(2)方法区中保存的Class对象没有被及时回收掉或者Class信息占用的内存超过了我们配置。
方法区的回收很困难,确定代码需要这么做后只能加大方法区的配置
4、本机直接内存溢出
直接内存的容量可以通过MaxDirectMemorySize来设置(默认与堆内存最大值一样),所以也会出现OOM异常;
由直接内存导致的内存溢出,一个比较明显的特征是在HeapDump文件中不会看见有什么明显的异常情况,如果发生了OOM,同时Dump文件很小,可以考虑重点排查下直接内存方面的原因。

对于OOM建议开启的VM参数(方便问题定位和排查)
生产服务器推荐开启
-XX:-HeapDumpOnOutOfMemoryError 默认关闭,建议开启,在java.lang.OutOfMemoryError 异常出现时,输出一个dump文件,记录当时的堆内存快照。
-XX:HeapDumpPath=./java_pid.hprof 用来设置堆内存快照的存储文件路径,默认是java进程启动位置。

二、导出dump文件

1.jvm启动时增加的参数
#出现 OOME 时生成堆 dump:
-XX:+HeapDumpOnOutOfMemoryError
#生成堆文件地址:
-XX:HeapDumpPath=/home/liuke/jvmlogs/
2.查看内存状态
jmap -heap 进程ID
3.查看JVM堆中对象详情占用情况
jmap -histo 进程ID
4.导出整个JVM 中内存信息,可以利用其它工具打开dump文件分析,例如jdk自带的MAT工具
jmap -dump:file=文件名.dump [pid]
jmap -dump:format=b,file=文件名 [pid]
format=b指定为二进制格式文件

你可能感兴趣的:(jvm,jvm)