代码之家  ›  专栏  ›  技术社区  ›  Koray Tugay

为什么这个小型Java程序使MACOS重新启动?

  •  7
  • Koray Tugay  · 技术社区  · 6 年前

    代码如下

    Set<Thread> threads = new HashSet<>();
    
    Runnable r = () -> {
        try {
            Thread.sleep(Long.MAX_VALUE);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    };
    
    for (int i = 0; i < 20000; i++) {
        Thread t = new Thread(r);
        threads.add(t);
        t.start();
        if (i % 100 == 0) {
            System.out.println(i);
        }
        Thread.sleep(2);
    }
    

    执行时,我开始看到如下值

    0
    100
    200
    300
    

    正如所料,直到我看到:

    3900
    4000
    Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
        at java.lang.Thread.start0(Native Method)
        at java.lang.Thread.start(Thread.java:717)
        at App.main(scratch.java:24)
    Java HotSpot(TM) 64-Bit Server VM warning: Exception java.lang.OutOfMemoryError occurred dispatching signal SIGINT to handler- the VM may need to be forcibly terminated
    

    但过了一会儿(10-20秒左右),MacOS决定重启。我在这里看到的重启原因是什么?主线程抛出异常,但进程有约4000个线程休眠导致…操作系统中有什么?这是内存溢出还是与操作系统的任务调度程序有关?

    MacOS version: 10.14.3 (18D109)
    java version "1.8.0_202"
    Java(TM) SE Runtime Environment (build 1.8.0_202-b08)
    Java HotSpot(TM) 64-Bit Server VM (build 25.202-b08, mixed mode)
    
    0 回复  |  直到 6 年前
        1
  •  7
  •   Badaro    6 年前

    尽管控制台显示程序已经完成,但jvm进程仍在运行,直到释放所有资源。同时,您的操作系统没有线程,速度慢而且不稳定,这会导致所有进程的延迟,包括jvm终结。作为自卫,操作系统引发了内核恐慌。这就是你的macos重启的原因。

    *操作系统

        2
  •  2
  •   Soutzikevich    6 年前

    Java是在90的时候构建的,当时只有多核处理器。

    当然Java已经进化,就像现代处理器一样。现在我们有8核处理器,有大缓存(例如:12MB)。

    尽管并发处理已经有了很大的发展,Java仍然围绕着1-核心处理器模型设计。但是,就历史而言,让我非常简单地解释一下发生了什么。

    仅仅通过在Java中创建一个新线程,我们就浪费了大量内存。

    每根线都会消耗掉 约512kb-1Mb ,具体取决于您的jvm版本(请参见 how much memory a thread takes in java Java Thread: Retained Memory )记住这一点,当不断创建新线程时,在某个时刻它们将消耗堆的所有内存。

    现在,我从来没有单独尝试过,但我假设您的计算机的操作系统由于“内存不足”错误而关闭/重新启动,以此作为对策。(这很像 triple fault ,这导致了 “死亡蓝屏” 在Windows上,计算机需要重新启动以重置CPU的状态)

    一种可能的解决方案是手动设置jvm使用的最大堆大小。因此,当您的程序完全使用预先分配的堆时,它不会导致关闭。请参考 this 所以关于怎么做的问题。

        3
  •  1
  •   ErikWi    6 年前

    这是一个 Fork bomb 变体。这可能会导致严重的速度减慢,但任何用户程序都不应该导致操作系统崩溃。这可能是操作系统中的错误或内存错误。尝试运行内存检查?

        4
  •  -4
  •   Sina Madani    6 年前

    很可能是因为您没有给jvm足够的内存,或者您的计算机硬件和macos的组合不允许同时有那么多线程处于活动状态。这个问题不仅限于macos,一些linux发行版,比如bodhi-linux,也有这个限制。不要被“outofmemoryerror”欺骗——这通常意味着jvm无法分配本机线程。