ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

发生 OOM 时 JVM 进程就退出吗

2022-06-27 23:06:18  阅读:153  来源: 互联网

标签:thread Thread OOM println 线程 JVM 进程 new public


不一定,要看是否还有存活的线程,如果有则 JVM 进程不会退出,否则才会退出

JVM 进程什么时候退出

需要明确,程序是否退出和发生 OOM 无关,而和当前是否还有存活的非守护线程有关。

只要还有运行中的子线程,即使 main 线程结束或异常崩溃了,程序也不会停止。

public class TestThreadRun {
    private static class MyTask implements Runnable {

        @Override
        public void run() {
            while (true) {
                System.out.println(String.format("我是子线程【%s】 每秒执行一次", Thread.currentThread().getName()));
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        Thread thread = new Thread(new MyTask(), "sub-thread-1");
//        thread.setDaemon(true);
        thread.start();
        System.out.println("main 线程结束");
        throw new RuntimeException("抛个异常");
    }
}

image

发生 OOM 时线程及其持有资源发生什么

当线程创建对象时,如果内存不足,会进行 GC,若GC 后还是内存不足,JVM 就会抛出OutOfMemoryError,如果线程没有处理异常,异常会继续向外层抛出,kill 掉线程,线程本身和其持有的资源就可以被回收了。

OOM 异常只会导致当前线程结束,对其他线程无影响。

后续其他线程再申请内存空间时,可能由于空间不足会发起 GC,然后就能够回收掉被关闭的线程和相关资源。

但是,线程被 kill 掉后,线程持有的资源只是可以被回收了,并不是一定能够回收,如果这些资源被其他线程持有,或者是一些全局变量、静态变量等不容易被 GC 的对象,那么堆内存空间占用依然会比较高。

这时候,其他线程再申请内存时,会继续发生 OOM 直至所有线程都结束,程序退出。

模拟 OOM 虚拟机配置:

-Xmx50M 
-Xms50M 
-XX:+HeapDumpOnOutOfMemoryError 
-XX:+PrintGCDetails 
-XX:+PrintGCDateStamps 
-XX:HeapDumpPath=/data/oom/dump/

a) 资源可以被回收的情况

测试代码:

public class TestThreadOOM {

    private static class Task1 implements Runnable {
        List<byte[]> list = new ArrayList<>(); // 资源与线程绑定
        @Override
        public void run() {
            while (true) {
                System.out.println(String.format("我是子线程【%s】 每秒创建一些字节数组", Thread.currentThread().getName()));
                list.add(new byte[1024*1024*5]);
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private static class Task2 implements Runnable {
        @Override
        public void run() {
            while (true) {
                System.out.println(String.format("我是子线程【%s】 每秒执行一次", Thread.currentThread().getName()));
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Task1(), "thread-1");
        thread.start();
        Thread thread2 = new Thread(new Task2(), "thread-2");
        thread2.start();
        thread.join();
        thread2.join();

        System.out.println("main 线程结束");
    }
}

image

image

image

根据时间线,可以看到,当线程发送 OOM 后,资源并没有立即被回收掉,而是下次 GC 时回收

b) 资源回收不了的情况

代码和上面差不多,只是 List 调整为静态变量

public class TestThreadOOM {
    static List<byte[]> list = new ArrayList<>(); // 使用静态变量 不会被回收

    private static class Task1 implements Runnable {

        @Override
        public void run() {
            while (true) {
                System.out.println(String.format("我是子线程【%s】 每秒创建一些字节数组", Thread.currentThread().getName()));

                list.add(new byte[1024*1024*5]);

                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private static class Task2 implements Runnable {

        private AtomicInteger i = new AtomicInteger(0);
        @Override
        public void run() {
            while (true) {
                System.out.println(String.format("我是子线程【%s】 每秒执行一次", Thread.currentThread().getName()));

                if (i.getAndIncrement() >= 10) { // 确保线程1 oom了
                    byte[] buf = new byte[1024*1024 * 20]; // 申请一些内存
                }
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Task1(), "thread-1");
        thread.start();

        Thread thread2 = new Thread(new Task2(), "thread-2");
        thread2.start();
        thread.join();
        thread2.join();


        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(String.format("【%s】申请内存", Thread.currentThread().getName()));
        byte[] buf = new byte[1024*1024 * 20]; // 申请一些内存

        System.out.println("main 线程结束");
    }
}

image

image

最后补充,OOM 异常是可以被捕获的,捕获异常后可以尝试继续执行

标签:thread,Thread,OOM,println,线程,JVM,进程,new,public
来源: https://www.cnblogs.com/liuyiyuan/p/16417844.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有