• 发生OOM时JVM会退出吗


    程序是否退出和发生 OOM 无关

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

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

    1. public class TestThreadRun {
    2. private static class MyTask implements Runnable {
    3. @Override
    4. public void run() {
    5. while (true) {
    6. System.out.println(String.format("我是子线程【%s】 每秒执行一次", Thread.currentThread().getName()));
    7. try {
    8. TimeUnit.SECONDS.sleep(1);
    9. } catch (InterruptedException e) {
    10. e.printStackTrace();
    11. }
    12. }
    13. }
    14. }
    15. public static void main(String[] args) {
    16. Thread thread = new Thread(new MyTask(), "sub-thread-1");
    17. // thread.setDaemon(true);
    18. thread.start();
    19. System.out.println("main 线程结束");
    20. throw new RuntimeException("抛个异常");
    21. }
    22. }

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

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

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

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

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

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

    资源可以被回收的情况

    1. public class TestThreadOOM {
    2. private static class Task1 implements Runnable {
    3. List<byte[]> list = new ArrayList<>(); // 资源与线程绑定
    4. @Override
    5. public void run() {
    6. while (true) {
    7. System.out.println(String.format("我是子线程【%s】 每秒创建一些字节数组", Thread.currentThread().getName()));
    8. list.add(new byte[1024*1024*5]);
    9. try {
    10. TimeUnit.SECONDS.sleep(1);
    11. } catch (InterruptedException e) {
    12. e.printStackTrace();
    13. }
    14. }
    15. }
    16. }
    17. private static class Task2 implements Runnable {
    18. @Override
    19. public void run() {
    20. while (true) {
    21. System.out.println(String.format("我是子线程【%s】 每秒执行一次", Thread.currentThread().getName()));
    22. try {
    23. TimeUnit.SECONDS.sleep(1);
    24. } catch (InterruptedException e) {
    25. e.printStackTrace();
    26. }
    27. }
    28. }
    29. }
    30. public static void main(String[] args) throws InterruptedException {
    31. Thread thread = new Thread(new Task1(), "thread-1");
    32. thread.start();
    33. Thread thread2 = new Thread(new Task2(), "thread-2");
    34. thread2.start();
    35. thread.join();
    36. thread2.join();
    37. System.out.println("main 线程结束");
    38. }
    39. }

    资源回收不了的情况

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

    1. public class TestThreadOOM {
    2. static List<byte[]> list = new ArrayList<>(); // 使用静态变量 不会被回收
    3. private static class Task1 implements Runnable {
    4. @Override
    5. public void run() {
    6. while (true) {
    7. System.out.println(String.format("我是子线程【%s】 每秒创建一些字节数组", Thread.currentThread().getName()));
    8. list.add(new byte[1024*1024*5]);
    9. try {
    10. TimeUnit.SECONDS.sleep(1);
    11. } catch (InterruptedException e) {
    12. e.printStackTrace();
    13. }
    14. }
    15. }
    16. }
    17. private static class Task2 implements Runnable {
    18. private AtomicInteger i = new AtomicInteger(0);
    19. @Override
    20. public void run() {
    21. while (true) {
    22. System.out.println(String.format("我是子线程【%s】 每秒执行一次", Thread.currentThread().getName()));
    23. if (i.getAndIncrement() >= 10) { // 确保线程1 oom了
    24. byte[] buf = new byte[1024*1024 * 20]; // 申请一些内存
    25. }
    26. try {
    27. TimeUnit.SECONDS.sleep(1);
    28. } catch (InterruptedException e) {
    29. e.printStackTrace();
    30. }
    31. }
    32. }
    33. }
    34. public static void main(String[] args) throws InterruptedException {
    35. Thread thread = new Thread(new Task1(), "thread-1");
    36. thread.start();
    37. Thread thread2 = new Thread(new Task2(), "thread-2");
    38. thread2.start();
    39. thread.join();
    40. thread2.join();
    41. try {
    42. TimeUnit.SECONDS.sleep(2);
    43. } catch (InterruptedException e) {
    44. e.printStackTrace();
    45. }
    46. System.out.println(String.format("【%s】申请内存", Thread.currentThread().getName()));
    47. byte[] buf = new byte[1024*1024 * 20]; // 申请一些内存
    48. System.out.println("main 线程结束");
    49. }
    50. }

     

  • 相关阅读:
    Git常用命令及解释
    Mac中隐私安全性设置-打开任何来源
    3.【异步通信框架】RabbitMQ
    美国国防部更“青睐”光量子系统研究路线
    有向图、无向图相关数据结构
    Pytorch实用教程:为什么定义模型时,没有输入形参,但是使用时可以直接传入输入数据?
    基础的SpringMvc开发
    dotnet-cnblog|迁移Gitee图床图片或上传本地图片到博客园中
    机器学习_10、集成学习-AdaBoost
    centos7 快速搭建自测mysql环境 docker + mysql
  • 原文地址:https://blog.csdn.net/weixin_35973945/article/details/132668294