Perché questo piccolo programma Java fa riavviare macOS?


Il codice

È il seguente

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

Quando eseguito, inizio a vedere valori come

0
100
200
300

Come previsto, e va fino a quando non vedo:

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

Ma dopo poco tempo (10 - 20 secondi circa) macOS decide di riavviare. Qual è la causa del riavvio che sto vedendo qui? Il thread principale genera un'eccezione, ma il processo con ~4000 thread che dormono causa ... cosa nel sistema operativo? Si tratta di un overflow di memoria o correlato all'utilità di pianificazione del 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)
Author: Koray Tugay, 2019-02-16

5 answers

Nonostante la console mostri che il programma è terminato, il processo JVM continua a funzionare fino al rilascio di tutte le risorse. Nel frattempo, il tuo sistema operativo è fuori dai thread, lento e instabile, che causano ritardi in tutti i processi, inclusa la finalizzazione della JVM. Come autodifesa, il sistema operativo innesca un kernel panic. Ed è per questo che il tuo macOS si riavvia.

* OS-Sistema operativo

 9
Author: Badaro, 2019-02-25 17:08:53

Java è stato costruito negli anni ' 90, quando c'erano solo processori multi-core.

Sicuramente Java si è evoluto, così come i processori moderni. Al giorno d'oggi abbiamo processori a 8 core, con cache di grandi dimensioni (ad esempio: 12 MB).

Anche se l'elaborazione simultanea si è evoluta molto, Java è ancora progettato attorno al modello di processore a 1 core. Ma, basta con la storia, lasciatemi spiegare molto molto semplicemente cosa succede.

Semplicemente creando un nuovo thread in Java, sprechiamo un sacco di memoria.

Ogni thread consuma circa ~ 512KB - 1MB, a seconda della versione JVM (vedere quanta memoria un thread prende in javae Thread Java: memoria mantenuta). Tenendo presente questo, quando si creano continuamente nuovi thread, ad un certo punto consumeranno tutta la memoria dell'heap.

Ora, non l'ho mai provato da solo, ma presumo che il sistema operativo del tuo computer si spenga / si riavvii a causa dell'errore "memoria esaurita", come contromisura. (Questo è molto simile al triple fault , che ha causato il famigerato "Blue Screen of Death" su Windows, dove la macchina aveva bisogno di riavviare per ripristinare lo stato della CPU)

Una possibile soluzione per questo, è impostare manualmente la dimensione massima dell'heap da utilizzare dalla JVM. Pertanto, quando il tuo programma utilizza completamente l'heap pre-allocato, non causerà un arresto. Si prega di fare riferimento a questo QUINDI domanda su come fare questo.

 3
Author: Soutzikevich, 2019-02-25 21:06:43

Mi sono appena imbattuto nello stesso problema oggi. Ecco alcuni codici python che innescano lo stesso kernel panic su 10.15.5 (Catalina). Testato su due mac per assicurarsi che questo non è un problema hardware:

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

Forse vado a scrivere una segnalazione di bug.

 1
Author: ephes, 2020-07-07 14:53:21

Questa è una variante Fork bomb. Ciò può causare un grave rallentamento, ma nessun programma utente dovrebbe essere in grado di bloccare il sistema operativo. Questo è probabilmente un bug nel sistema operativo o errore di memoria. Prova a eseguire un controllo della memoria?

 0
Author: ErikWi, 2019-02-24 05:42:19

Molto probabilmente perché non hai dato alla tua JVM abbastanza memoria, o la combinazione hardware e macOS del tuo computer non consente che molti thread siano attivi contemporaneamente. Questo problema non è limitato a macOS, ma alcune distribuzioni Linux, ad esempio Bodhi Linux, hanno anche questa limitazione. Non essere ingannato da "OutOfMemoryError" - questo può spesso significare che la JVM non è stata in grado di allocare un thread nativo.

 -4
Author: Sina Madani, 2019-02-16 17:16:45