Come gestire con garbo il segnale SIGKILL in Java

Come gestisci la pulizia quando il programma riceve un segnale di uccisione?

Ad esempio, c’è una domanda a cui mi collego che vuole qualsiasi app di terze parti (la mia app) per inviare un comando di finish quando si disconnette. Qual è la cosa migliore da dire per inviare il comando di finish quando la mia app è stata distrutta con un kill -9 ?

modifica 1: kill -9 non può essere catturato. Grazie ragazzi per avermi corretto.

modifica 2: Immagino che questo caso si verifichi quando quello chiama solo kill che è lo stesso di ctrl-c

Il modo di gestirlo per qualcosa di diverso da kill -9 sarebbe quello di registrare un hook di shutdown . Se è ansible utilizzare ( SIGTERM ) kill -15 il hook di arresto funzionerà. ( SIGINT ) kill -2 DOES fa in modo che il programma esca con garbo ed esegua i hook di shutdown.

Registra un nuovo hook di spegnimento della macchina virtuale.

La macchina virtuale Java si spegne in risposta a due tipi di eventi:

 * The program exits normally, when the last non-daemon thread exits or 

quando viene invocato il metodo exit (equivalentemente, System.exit), o

 * The virtual machine is terminated in response to a user 

interrupt, ad esempio digitare ^ C o un evento a livello di sistema, ad esempio la disconnessione dell’utente o l’arresto del sistema.

Ho provato il seguente programma di test su OSX 10.6.3 e su kill -9 NON ha eseguito il hook di shutdown, non pensavo che sarebbe successo. Su un kill -15 esegue il hook shutdown ogni volta.

 public class TestShutdownHook { public static void main(final String[] args) throws InterruptedException { Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { System.out.println("Shutdown hook ran!"); } }); while (true) { Thread.sleep(1000); } } } 

Non c’è alcun modo per gestire con grazia un kill -9 in qualsiasi programma.

In rare circostanze, la macchina virtuale può interrompersi, ovvero interrompere l’esecuzione senza spegnersi in modo pulito. Ciò si verifica quando la macchina virtuale viene terminata esternamente, ad esempio con il segnale SIGKILL su Unix o la chiamata TerminateProcess su Microsoft Windows.

L’unica vera opzione per gestire un kill -9 consiste nell’avere un altro programma watcher per vedere se il tuo programma principale se ne va o usare uno script wrapper. Si potrebbe fare con questo con uno script di shell che ha interrogato il comando ps cercando il tuo programma nell’elenco e agendo di conseguenza quando è scomparso.

 #!/usr/bin/env bash java TestShutdownHook wait # notify your other app that you quit echo "TestShutdownHook quit" 

Esistono modi per gestire i tuoi segnali in determinate JVM: consulta questo articolo su HotSpot JVM, ad esempio.

Usando la chiamata al metodo interno Sun sun.misc.Signal.handle(Signal, SignalHandler) si è anche in grado di registrare un gestore di segnale, ma probabilmente non per segnali come INT o TERM come vengono usati dalla JVM.

Per essere in grado di gestire qualsiasi segnale si dovrebbe saltare fuori dalla JVM e nel territorio del Sistema Operativo.

Quello che generalmente faccio per (per esempio) rilevare terminazioni anomale è avviare la mia JVM all’interno di uno script Perl, ma fare in modo che lo script attenda che la JVM usi la waitpid sistema waitpid .

Vengo quindi informato ogni volta che esce JVM e perché è uscito e può intraprendere le azioni necessarie.

È ansible utilizzare Runtime.getRuntime().addShutdownHook(...) , ma non è ansible garantire che venga chiamato in ogni caso .

Mi aspetto che JVM interrompa con garbo ( thread.interrupt() ) tutti i thread in esecuzione creati dall’applicazione, almeno per i segnali SIGINT (kill -2) e SIGTERM (kill -15) .

In questo modo, il segnale verrà inoltrato a loro, consentendo una cancellazione del thread e la finalizzazione delle risorse in modo normale nei modi standard .

Ma questo non è il caso (almeno nella mia implementazione JVM: Java(TM) SE Runtime Environment (build 1.8.0_25-b17), Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode) .

Come altri utenti hanno commentato, l’uso dei ganci di shutdown sembra obbligatorio.

Quindi, come lo gestirò?

Beh, prima di tutto, non mi interessa in tutti i programmi, solo in quelli in cui voglio tenere traccia delle cancellazioni degli utenti e dei fini imprevisti. Ad esempio, immagina che il tuo programma java sia un processo gestito da altri. È ansible che si desideri distinguere se è stato terminato SIGTERM ( SIGTERM dal processo di gestione) o si è verificato un arresto (al fine di riavviare automaticamente il lavoro all’avvio).

Come base, faccio sempre in modo che i miei thread di lunga durata siano periodicamente al corrente dello stato interrotto e lanciano un’interruptuptionException se si interrompono. Ciò consente la finalizzazione dell’esecuzione in modo controllato dallo sviluppatore (producendo anche lo stesso risultato delle operazioni di blocco standard). Quindi, al livello più alto della pila di fili, viene InterruptedException viene eseguita la pulizia appropriata. Questi thread sono codificati per sapere come rispondere a una richiesta di interruzione. Design ad alta coesione .

Quindi, in questi casi, aggiungo un hook di chiusura, che fa quello che penso che la JVM dovrebbe fare di default: interrompe tutti i thread non daemon creati dalla mia applicazione ancora in esecuzione:

 Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { System.out.println("Interrupting threads"); Set runningThreads = Thread.getAllStackTraces().keySet(); for (Thread th : runningThreads) { if (th != Thread.currentThread() && !th.isDaemon() && th.getClass().getName().startsWith("org.brutusin")) { System.out.println("Interrupting '" + th.getClass() + "' termination"); th.interrupt(); } } for (Thread th : runningThreads) { try { if (th != Thread.currentThread() && !th.isDaemon() && th.isInterrupted()) { System.out.println("Waiting '" + th.getName() + "' termination"); th.join(); } } catch (InterruptedException ex) { System.out.println("Shutdown interrupted"); } } System.out.println("Shutdown finished"); } }); 

Completa l’applicazione di test su github: https://github.com/idelvall/kill-test

C’è un modo per reactjs a un kill -9: cioè avere un processo separato che monitora il processo che viene ucciso e ripulisce dopo di esso se necessario. Ciò probabilmente implicherebbe l’IPC e sarebbe un bel po ‘di lavoro, e puoi comunque ignorarlo uccidendo entrambi i processi allo stesso tempo. Presumo che non ne varrà la pena nella maggior parte dei casi.

Chiunque uccida un processo con -9 dovrebbe teoricamente sapere che cosa sta facendo e che potrebbe lasciare le cose in uno stato incoerente.