Why does this small Java program make MacOS restart?

Code is as follows

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);
}

When executed, I start seeing values like

0
100
200
300

as expected, and it goes until I see:

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

But then after a short while (10 - 20 seconds or so) MacOS decides to restart. What is the cause for the restart I am seeing here? The main thread throwing an exception, but the process having ~4000 threads sleeping causes ... what in the operating system? Is this a memory overflow or related to task scheduler of the OS?

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)

Despite the fact that the console shows the program has finished, the JVM process still runs until all resources are released. Meanwhile, your OS is out of threads, slow and unstable, which cause lagging in all processes, including the JVM finalization. As a self defense, the OS triggers a kernel panic. And that is why your MacOS restarts.

*OS - Operating System


Java was built in the 90's, when there were only multi-core processors.

Surely Java has evolved, as have modern-day processors. Nowadays we have 8-core processors, with large caches (e.g: 12MB).

Even though concurrent processing has evolved much, Java is still designed around the 1-core processor model. But, enough with history, let me explain very very simply what happens.

Just by simply creating a new thread in Java, we waste a lot of memory.

Every thread consumes around ~ 512KB - 1MB, depending on your JVM version (see how much memory a thread takes in java and Java Thread: Retained Memory). Bearing this in mind, when continuously creating new Threads, at some point they will consume all of the heap's memory.

Now, I have never tried this on my own, but I assume that your computer's operating system shuts down/restarts due to the "out of memory" error, as a countermeasure. (This is much like the triple fault, that caused the infamous "Blue Screen of Death" on Windows, where the machine needed to restart to reset the CPU's state)

One possible solution for this, is to manually set the maximum heap size to be used by the JVM. Thus, when your program completely utilises the pre-allocated heap, it will not cause a shutdown. Please refer to this SO question on how to do this.


I just stumbled on the same problem today. Here's some python code triggering the same kernel panic on 10.15.5 (Catalina). Tested it on two macs to make sure this is not a hardware problem:

https://github.com/ephes/django_async/blob/master/measure_threads_memory.py

Maybe I go and write a bug report.


Cause of the restart is a counter measure by OS, when OS becomes CPU heavy and threads consumes the OS resources. To be specific,"unable to create new native thread" specifies that OS is out of threads and can't create further threads.

Also, to be noted, A JVM runs in a single process and threads in a JVM share the heap belonging to that process. Java will utilize the underlying OS threads to do the actual job of executing the code on different CPUs, if running on a multi-CPU machine. When each Java thread is started, it creates an associated OS thread and the OS is responsible for scheduling, etc.

OS is smart enough to make use of available cores for thread execution based on priority and other scheduling algorithms.

And, "java.lang.OutOfMemoryError" signifies, that OS has utilized the heap memory allocated or as specified in JVM heap size.

So, if the heap size is large and available memory is not enough for other processes in OS causing the lag and eventually in order to reset the CPU state, OS restarts.