Come si blocca una JVM?

Stavo leggendo un libro sulle abilità di programmazione in cui l’autore chiede all’intervistato: “Come si fa a schiantare una JVM?” Pensavo che potessi farlo scrivendo un ciclo infinito che alla fine avrebbe esaurito tutta la memoria.

Qualcuno ha qualche idea?

La cosa più vicina a una singola “risposta” è System.exit() che termina immediatamente la JVM senza una corretta pulizia. Ma a parte questo, il codice nativo e l’esaurimento delle risorse sono le risposte più probabili. In alternativa, puoi cercare sul bug tracker di Sun i bug nella tua versione della JVM, alcuni dei quali consentono scenari di arresto ripetibili. Avevamo degli arresti semi-regolari quando ci si avvicinava al limite di memoria di 4 Gb sotto le versioni a 32 bit (generalmente usiamo 64-bit ora).

Non chiamerei lanciare un errore OutOfMemoryError o StackOverflowError. Queste sono solo normali eccezioni. Per bloccare davvero una VM ci sono 3 modi:

  1. Usa JNI e crash nel codice nativo.
  2. Se non è installato alcun gestore della sicurezza, è ansible utilizzare reflection per arrestare la VM. Questo è specifico della VM, ma normalmente una VM memorizza una serie di puntatori a risorse native in campi privati ​​(ad esempio un puntatore all’object thread nativo è memorizzato in un campo lungo in java.lang.Thread ). Basta cambiarli tramite riflessione e la VM si bloccherà prima o poi.
  3. Tutte le macchine virtuali hanno dei bug, quindi devi innescarne uno.

Per l’ultimo metodo ho un breve esempio, che farà schiantare un Sun Hotspot VM tranquillamente:

 public class Crash { public static void main(String[] args) { Object[] o = null; while (true) { o = new Object[] {o}; } } } 

Questo porta ad un overflow dello stack nel GC, quindi non si otterrà StackOverflowError ma un crash reale che include un file hs_err *.

JNI . In effetti, con JNI, l’arresto anomalo è la modalità operativa predefinita. Devi lavorare molto duramente per non farlo andare in crash.

Usa questo:

 import sun.misc.Unsafe; public class Crash { private static final Unsafe unsafe = Unsafe.getUnsafe(); public static void crash() { unsafe.putAddress(0, 0); } public static void main(String[] args) { crash(); } } 

Questa class deve trovarsi sul percorso di class di avvio perché sta utilizzando il codice attendibile, quindi esegui in questo modo:

java -Xbootclasspath / p :. schianto

Sono venuto qui perché mi sono imbattuto in questa domanda in The Passionate Programmer , di Chad Fowler. Per coloro che non hanno accesso a una copia, la domanda viene inquadrata come una sorta di filtro / test per i candidati che intervistano per una posizione che richiede “programmatori Java veramente buoni”.

Nello specifico, chiede:

Come scriveresti un programma, in puro Java, che causerebbe l’arresto anomalo della Java Virtual Machine?

Ho programmato in Java per oltre 15 anni, e ho trovato questa domanda sia al tempo stesso sconcertante e ingiusta. Come altri hanno sottolineato, Java, come linguaggio gestito, è progettato specificamente per non andare in crash . Ovviamente ci sono sempre bug JVM, ma:

  1. Dopo oltre 15 anni di JRE a livello di produzione, è raro.
  2. È probabile che eventuali bug di questo tipo vengano corretti nella prossima versione, quindi quanto è probabile che tu sia un programmatore in cui incorrere e richiami i dettagli dell’attuale serie di show-stoper di JRE?

Come altri hanno già menzionato, alcuni codici nativi tramite JNI sono un modo sicuro per bloccare un JRE. Ma l’autore ha menzionato specificamente in puro Java , quindi è fuori.

Un’altra opzione sarebbe quella di alimentare i codici byte fasulli JRE; è abbastanza facile scaricare alcuni dati binari della spazzatura in un file .class e chiedere a JRE di eseguirlo:

 $ echo 'crap crap crap' > crap.class $ java crap Exception in thread "main" java.lang.ClassFormatError: Incompatible magic value 1668440432 in class file crap 

Conta? Intendo che il JRE stesso non si è schiantato; ha rilevato correttamente il codice falso, lo ha segnalato ed è uscito.

Questo ci lascia con i tipi più ovvi di soluzioni come far esplodere lo stack tramite la ricorsione, l’esaurimento della memoria heap tramite allocazioni di oggetti o semplicemente il lancio di RuntimeException . Ma questo fa sì che JRE esca con un StackOverflowError o un’eccezione simile, che, di nuovo, non è realmente un crash .

Quindi cosa rimane? Mi piacerebbe davvero sentire ciò che l’autore aveva davvero in mente come soluzione adeguata.

Aggiornamento : Chad Fowler ha risposto qui .

PS: è un libro altrimenti eccezionale. L’ho preso per il supporto morale mentre stavo imparando Ruby.

Questo codice bloccherà la JVM in modi cattivi

 import sun.dc.pr.PathDasher; public class Crash { public static void main(String[] args) { PathDasher dasher = new PathDasher(null) ; } } 

L’ultima volta che ho provato questo lo farebbe:

 public class Recur { public static void main(String[] argv) { try { recur(); } catch (Error e) { System.out.println(e.toString()); } System.out.println("Ended normally"); } static void recur() { Object[] o = null; try { while(true) { Object[] newO = new Object[1]; newO[0] = o; o = newO; } } finally { recur(); } } } 

Prima parte del file di registro generato:

 # # An unexpected error has been detected by Java Runtime Environment: # # EXCEPTION_STACK_OVERFLOW (0xc00000fd) at pc=0x000000006dad5c3d, pid=6752, tid=1996 # # Java VM: Java HotSpot(TM) 64-Bit Server VM (11.2-b01 mixed mode windows-amd64) # Problematic frame: # V [jvm.dll+0x2e5c3d] # # If you would like to submit a bug report, please visit: # http://java.sun.com/webapps/bugreport/crash.jsp # --------------- THREAD --------------- Current thread (0x00000000014c6000): VMThread [stack: 0x0000000049810000,0x0000000049910000] [id=1996] siginfo: ExceptionCode=0xc00000fd, ExceptionInformation=0x0000000000000001 0x0000000049813fe8 Registers: EAX=0x000000006dc83090, EBX=0x000000003680f400, ECX=0x0000000005d40ce8, EDX=0x000000003680f400 ESP=0x0000000049813ff0, EBP=0x00000000013f2df0, ESI=0x00000000013f0e40, EDI=0x000000003680f400 EIP=0x000000006dad5c3d, EFLAGS=0x0000000000010206 

Un’implementazione JVM perfetta non si arresterà mai.

Per bloccare una JVM, a parte JNI, devi trovare un bug nella VM stessa. Un ciclo infinito consuma solo CPU. L’allocazione infinita della memoria dovrebbe solo causare OutOfMemoryError in una JVM ben costruita. Ciò probabilmente causerebbe problemi ad altri thread, ma una buona JVM non dovrebbe comunque bloccarsi.

Se è ansible trovare un bug nel codice sorgente della VM e, ad esempio, causare un errore di segmentazione nell’utilizzo della memoria dell’implementazione della VM, è ansible bloccarlo.

Se si desidera arrestare in modo anomalo JVM, utilizzare quanto segue in Sun JDK 1.6_23 o versioni precedenti:

 Double.parseDouble("2.2250738585072012e-308"); 

Ciò è dovuto a un bug in Sun JDK, anch’esso trovato in OpenJDK. Questo problema è stato risolto da Oracle JDK 1.6_24 in poi.

Dipende da cosa intendi per incidente.

Puoi fare una ricorsione infinita per farlo esaurire lo spazio di stack, ma questo si bloccherà “con grazia”. Riceverai un’eccezione, ma la JVM stessa gestirà tutto.

Puoi anche usare JNI per chiamare il codice nativo. Se non lo fai bene, puoi farlo diventare duro. Il debug di questi arresti anomali è “divertente” (credetemi, ho dovuto scrivere una grande DLL C ++ che chiamiamo da un’applet java firmata). 🙂

Il libro Java Virtual Machine di Jon Meyer ha un esempio di una serie di istruzioni bytecode che hanno causato il core dump della JVM. Non riesco a trovare la mia copia di questo libro. Se qualcuno là fuori ne ha uno, per favore, cercalo e posta la risposta.

su winxpsp2 con wmp10 jre6.0_7

Desktop.open (uriToAviOrMpgFile)

Questo fa sì che un thread generato generi un Throwable non bloccato e blocca l’hotspot

YMMV

L’hardware rotto può bloccare qualsiasi programma. Una volta ho avuto un crash dell’app in modo riproducibile su una macchina specifica mentre funzionavo bene su altre macchine con la stessa identica configurazione. Risulta che la macchina aveva una RAM difettosa.

ecco una spiegazione dettagliata su cosa causa JVM al core dump (es. crash): http://kb.adobe.com/selfservice/viewContent.do?externalId=tn_17534

il modo più breve ansible 🙂

 public class Crash { public static void main(String[] args) { main(args); } } 

Se si definisce un arresto anomalo all’avvio di un processo a causa di una situazione non gestita (ad esempio, nessuna eccezione o errore Java), non è ansible eseguirlo da Java (a meno che non si disponga dell’authorization per utilizzare la class sun.misc.Unsafe). Questo è l’intero punto del codice gestito.

Arresti anomali tipici del codice nativo avvengono facendo de-referenziare i puntatori a aree di memoria sbagliate (indirizzo nullo o missaligned). Un’altra fonte potrebbe essere le istruzioni illegali della macchina (opcode) o i segnali non gestiti dalle chiamate alla libreria o al kernel. Entrambi possono essere triggersti ​​se la JVM o le librerie di sistema hanno bug.

Ad esempio il codice (generato) JITed, i metodi nativi o le chiamate di sistema (driver grafico) possono avere problemi che portano a veri arresti anomali (era abbastanza comune ottenere un arresto anomalo quando si utilizzavano le funzioni ZIP e si esauriva la memoria). In questi casi, il gestore degli arresti anomali della JVM entra in azione e scarica lo stato. Potrebbe anche generare un file di base del sistema operativo (Dr. Watson su Windows e core dump su * nix).

Su Linux / Unix puoi facilmente fare un arresto anomalo di JVM inviandogli un segnale per il processo in esecuzione. Nota: non devi usare SIGSEGV per questo, poiché Hotspot cattura questo segnale e lo rigira come NullPointerException nella maggior parte dei posti. Quindi è meglio inviare un SIGBUS per esempio.

Non un arresto anomalo, ma più vicino a un arresto anomalo rispetto alla risposta accettata per l’utilizzo di System.exit

È ansible interrompere la JVM chiamando

Runtime.getRuntime().halt( status )

Secondo i documenti: –

“questo metodo non causa l’avvio di hook di spegnimento e non esegue i finalizzatori non inviati se la finalizzazione all’uscita è stata abilitata”.

Se vuoi fingere di aver esaurito la memoria, puoi farlo

 public static void main(String[] args) { throw new OutOfmemoryError(); } 

Conosco un paio di modi per fare in modo che JVM esegua il dump di un file di errore chiamando metodi nativi (quelli che sono integrati), ma probabilmente è meglio non sapere come farlo. 😉

JNI è una grande fonte di arresti anomali. È anche ansible arrestarsi in modo anomalo utilizzando l’interfaccia JVMTI poiché è necessario scrivere anche in C / C ++.

Più breve? Usa la class Robot per triggersre CTRL + INTERR. Ho notato questo quando stavo cercando di chiudere il mio programma senza chiudere la console (non aveva funzionalità di “uscita”).

Se crei un processo di thread che genera in modo infinito più thread (che generano più thread, che …) alla fine causerai un errore di overflow dello stack nella JVM stessa.

 public class Crash { public static void main(String[] args) { Runnable[] arr = new Runnable[1]; arr[0] = () -> { while (true) { new Thread(arr[0]).start(); } }; arr[0].run(); } } 

Questo mi ha dato l’output (dopo 5 minuti, guarda la tua ram)

 An unrecoverable stack overflow has occurred. # # A fatal error has been detected by the Java Runtime Environment: # # EXCEPTION_STACK_OVERFLOW (0xc00000fd) at pc=0x0000000070e53ed7, pid=12840, tid=0x0000000000101078 # # JRE version: Java(TM) SE Runtime Environment (8.0_144-b01) (build 1.8.0_144-b01) # Java VM: Java HotSpot(TM) 64-Bit Server VM (25.144-b01 mixed mode windows-amd64 compressed oops) # Problematic frame: # 

Lo sto facendo ora, ma non del tutto sicuro di come … 🙂 JVM (e la mia app) a volte scompare completamente. Nessun errore generato, niente registrato. Passa dal lavorare per non correre affatto all’istante senza preavviso.

Se modifichi il loop infinito per una chiamata ricorsiva alla stessa funzione, otterrai un’eccezione di overflow dello stack:

 public static void main(String[] args) { causeStackOverflow(); } public void causeStackOverflow() { causeStackOverflow(); } 

Se un ‘Crash’ è qualcosa che interrompe il jvm / programma dalla terminazione normale, allora un’eccezione Un-handled potrebbe farlo.

 public static void main(String args[]){ int i = 1/0; System.out.print(i); // This part will not be executed due to above unhandled exception } 

Quindi, dipende da che tipo di CRASH ?!