为啥这个Java小程序会让MacOS重启?
Posted
技术标签:
【中文标题】为啥这个Java小程序会让MacOS重启?【英文标题】:Why does this small Java program make MacOS restart?为什么这个Java小程序会让MacOS重启? 【发布时间】:2019-07-10 11:37:39 【问题描述】:代码如下
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 版本:10.14.3 (18D109) java版本“1.8.0_202” Java(TM) SE 运行时环境 (build 1.8.0_202-b08) Java HotSpot(TM) 64 位服务器 VM(内部版本 25.202-b08,混合模式)【问题讨论】:
如果应用程序崩溃了,问题就不会那么大了。然而,如果一个程序能够触发系统重启,这是一个严重的与安全相关的操作系统错误(但也可能是硬件,例如内存问题 - 不要将每个故障都归咎于操作系统)。 无法在我的 android 手机上进行复制。我刚刚收到 OOM 错误。 @Carcigenicate 这就是为什么我特别指出我在 MacOS 上。我认为这与该程序运行的底层操作系统有关,而不是与 JVM 本身有关。我认为它只能在 MacOS 中重现。 (我不能尝试 Windows,因为我手头没有 Windows 机器。) 可以确认它在 MacOS Mojave、java 版本“1.8.0_202-ea”上崩溃,在 Windows 10 上似乎没有崩溃(虽然我使用了不同的机器)。 哈哈。在 100k 线程时,我遇到了蓝屏死机,然后它重新启动。也许这算作复制? 【参考方案1】:尽管控制台显示程序已完成,但 JVM 进程仍在运行,直到所有资源都被释放。同时,您的操作系统线程不足,速度慢且不稳定,这会导致所有进程滞后,包括 JVM 终结。作为自卫,操作系统触发内核恐慌。这就是您的 MacOS 重新启动的原因。
*OS - 操作系统
【讨论】:
【参考方案2】: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 SO question了解如何执行此操作。
【讨论】:
【参考方案3】:我今天偶然发现了同样的问题。这是一些在 10.15.5 (Catalina) 上触发相同内核恐慌的 python 代码。在两台 Mac 上进行了测试,以确保这不是硬件问题:
https://github.com/ephes/django_async/blob/master/measure_threads_memory.py
也许我去写一个错误报告。
【讨论】:
【参考方案4】:重启的原因是操作系统的一种应对措施,当操作系统变得 CPU 繁重并且线程消耗操作系统资源时。 具体来说,"unable to create new native thread" 表示操作系统没有线程,无法创建更多线程。
另外,需要注意的是,JVM 在单个进程中运行,并且 JVM 中的线程共享属于该进程的堆。 如果在多 CPU 机器上运行,Java 将利用底层操作系统线程来完成在不同 CPU 上执行代码的实际工作。每个Java线程启动时,都会创建一个关联的OS线程,由OS负责调度等。
操作系统足够智能,可以根据优先级和其他调度算法利用可用内核来执行线程。
并且,“java.lang.OutOfMemoryError”表示操作系统已使用分配的堆内存或 JVM 堆大小中指定的内存。
因此,如果堆大小很大并且可用内存不足以供 OS 中的其他进程使用,从而导致延迟并最终重置 CPU 状态,则 OS 会重新启动。
【讨论】:
【参考方案5】:这是一个Fork bomb 变体。这可能会导致严重的减速,但任何用户程序都不应该使操作系统崩溃。这可能是操作系统中的错误或内存错误。尝试运行内存检查?
【讨论】:
【参考方案6】:很可能是因为您没有为 JVM 提供足够的内存,或者您的计算机硬件和 macOS 组合不允许同时激活这么多线程。此问题不仅限于 macOS,还限于某些 Linux 发行版,例如Bodhi Linux,也有这个限制。不要被“OutOfMemoryError”所欺骗——这通常意味着 JVM 无法分配本地线程。
【讨论】:
我不认为do not permit that many threads to be active at once
不是重启的理由。
如果是“不允许”,那哪里不存在这样的问题。在 ops 的情况下,这不会发生,操作系统的“不允许”以某种方式被绕过,并且出现了操作系统本身中断的情况。这是一个严重的安全问题:非特权用户(至少在进程隔离方面)能够触发特权操作。
公平点。我想知道如果以较低的优先级运行程序并将程序中的线程优先级设置为最低会发生什么。以上是关于为啥这个Java小程序会让MacOS重启?的主要内容,如果未能解决你的问题,请参考以下文章
java 小程序误区。。FileInputStream in = null; try in = new FileInputStream(。。。。。为啥这么定