美文网首页后端汇总
发生OOM了程序一定会退出吗

发生OOM了程序一定会退出吗

作者: 少年_7b60 | 来源:发表于2020-03-07 21:43 被阅读0次

答案是不会,下面会给出分析和验证

先看下OOM

OOM全称OutOfMemoryError,是一个异常,属于Error系非受检异常,继承自
Throwable->Error->VirtualMachineError
当某一个线程中的在执行时申请不到足够的内存,就会抛出该异常

OOM具体分要分是哪里OOM的,常见的有,还有其他情况

  • 堆溢出 java.lang.OutOfMemoryError: Java heap space,
  • 永久带溢出 java.lang.OutOfMemoryError:Permgen space
  • 不能创建线程 java.lang.OutOfMemoryError:Unable to create new native thread

JVM何时会退出

当进程只有守护进程时

一个线程中发生堆OOM是否会导致JVM退出?

答案是不会

使用如下代码测试

/*-Xms16m -Xmx32m*/
    public static void main(String[] args) {

        long maxMemory = Runtime.getRuntime().maxMemory();
        long totalMemory=Runtime.getRuntime().totalMemory();

        System.out.println(String.format("maxMemory: (%.2f M)", (double) maxMemory / (1024 * 1024)));
        System.out.println(String.format("totalMemory: (%.2f M)", (double) totalMemory / (1024 * 1024)));

        new Thread(() -> {
            List<byte[]> list = new ArrayList<byte[]>();
            while (true) {
                System.out.println(Thread.currentThread() + " alloc memory");
                byte[] b = new byte[1024 * 10240 * 1];
                list.add(b);
                try {
                    Thread.sleep(3000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println(Thread.currentThread() + " echo");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                    }
                }

            }
        }).start();
    }

执行结果如图


当一个线程oom,代表堆不够用了,为什么其他线程不紧接着oom继而JVM退出呢

个人觉得有3方面原因:

  1. 一个线程的异常不会抛到其他线程
  2. 线程发生OOM后会部分资源被回收
  3. 发生OOM是由于申请内存触发的,如果其他线程不申请资源,则也不会OOM

第1个原因后续分析,先看内存回收情况
这段时间内存图如下


可以看到,当线程0抛出异常后,结果堆中内存立即得到了释放
这是因为线程0申请的对象仅被线程0所引用

此外,线程1 并没有申请任何资源,这意味着即使线程0OOM,且内存没被回收,线程1依然可以继续执行。将代码稍作修改,线程0 OOM后线程1依然可以继续执行,只是堆的空间维持在耗尽的状态,如图

补充

OOM可以catch吗?
可以,一切Throwable都可以catch

byte[] b = null;
try{
    b= new byte[1024 * 10240 * 1];
}catch (Error e){
    System.out.println(e.getMessage()+" "+b);
}

这段代码中,发生oom后,对象创建失败,会打印如下

Java heap space null

线程与异常的关系

  1. 线程不允许往外抛受检异常
    从方法签名public abstract void run();就可以看出
  2. 对于非受检异常,或是未能捕获的受检异常,会交给UncaughtExceptionHandler处理,具体过长如下
    当异常出现后,虚拟机调用
private void dispatchUncaughtException(Throwable e) {
        getUncaughtExceptionHandler().uncaughtException(this, e);
}
public UncaughtExceptionHandler getUncaughtExceptionHandler() {
        return uncaughtExceptionHandler != null ?
            uncaughtExceptionHandler : group;
    }

一个线程为主动设置UncaughtExceptionHandler时,会由当前线程的group处理,group在init方法中初始化,它是ThreadGroup对象,有能力处理未捕获异常
class ThreadGroup implements Thread.UncaughtExceptionHandler

public void uncaughtException(Thread t, Throwable e) {
        if (parent != null) {
            parent.uncaughtException(t, e);
        } else {
            Thread.UncaughtExceptionHandler ueh =
                Thread.getDefaultUncaughtExceptionHandler();
            if (ueh != null) {
                ueh.uncaughtException(t, e);
            } else if (!(e instanceof ThreadDeath)) {
                System.err.print("Exception in thread \""
                                 + t.getName() + "\" ");
                e.printStackTrace(System.err);
            }
        }
    }

从上面可以看出,它对异常的处理就是打印异常信息,结束后线程也就退出了

参考资料

https://mp.weixin.qq.com/s/8j8YTcr2qhVActLGzOqe7Q
https://blog.csdn.net/w372426096/article/details/82851375
https://www.cnblogs.com/myseries/p/12079708.html

相关文章

网友评论

    本文标题:发生OOM了程序一定会退出吗

    本文链接:https://www.haomeiwen.com/subject/qrlkdhtx.html