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)
Strictly speaking, a Java program cannot restart itself since to do so it must kill the JVM in which it is running and then start it again, but once the JVM is no longer running (killed) then no action can be taken.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With