Come fermare il processo Java con grazia?


88

Come si interrompe un processo Java con grazia in Linux e Windows?

Quando Runtime.getRuntime().addShutdownHookviene chiamato e quando no?

E i finalizzatori, aiutano qui?

Posso inviare una sorta di segnale a un processo Java da una shell?

Sto cercando soluzioni preferibilmente portatili.


dovresti davvero definire cosa intendi per con garbo. (per favore)
Henry B

7
Presumo che intendano dire che vogliono avere la possibilità di ripulire risorse, rilasciare, bloccare e scaricare tutti i dati persistenti su disco prima che il programma venga ucciso.
Steve g

Risposte:


82

Gli hook di arresto vengono eseguiti in tutti i casi in cui la VM non viene uccisa forzatamente. Quindi, se si dovesse emettere un kill "standard" ( SIGTERMda un comando kill), allora verranno eseguiti. Allo stesso modo, verranno eseguiti dopo la chiamata System.exit(int).

Tuttavia un hard kill ( kill -9o kill -SIGKILL) non verranno eseguiti. Allo stesso modo (e ovviamente) non verranno eseguiti se si estrae l'alimentazione dal computer, lo si lascia cadere in una vasca di lava bollente o si fa a pezzi la CPU con una mazza. Probabilmente lo sapevi già, però.

Anche i finalizzatori dovrebbero funzionare, ma è meglio non fare affidamento su questo per la pulizia dell'arresto, ma piuttosto fare affidamento sugli hook di arresto per fermare le cose in modo pulito. E, come sempre, fai attenzione ai deadlock (ho visto troppi ganci di arresto sospendere l'intero processo)!


1
Sfortunatamente, questo non sembra funzionare su Windows 7 (64 bit). Ho provato a utilizzare taskill, senza il flag di forzatura, e ho riscontrato il seguente errore: "ERRORE: impossibile terminare il processo con PID 14324. Motivo: questo processo può essere terminato solo forzatamente (con l'opzione / F)." Fornire l'opzione force, "/ f", ovviamente chiuderà il processo all'istante.
Jason Huntley

Non puoi fare affidamento sui finalizzatori in esecuzione.
Thorbjørn Ravn Andersen

47

Ok, dopo tutte le possibilità che ho scelto di lavorare con "Java Monitoring and Management"
Panoramica è qui
che ti permette di controllare un'applicazione da un'altra in modo relativamente semplice. È possibile chiamare l'applicazione di controllo da uno script per arrestare correttamente l'applicazione controllata prima di ucciderla.

Ecco il codice semplificato:

Applicazione controllata:
eseguirla con i seguenti parametri VM:
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port = 9999
-Dcom.sun.management.jmxremote.authenticate = false
-Dcom.sun.management. jmxremote.ssl = false

//ThreadMonitorMBean.java
public interface ThreadMonitorMBean
{
String getName();
void start();
void stop();
boolean isRunning();
}

// ThreadMonitor.java
public class ThreadMonitor implements ThreadMonitorMBean
{
private Thread m_thrd = null;

public ThreadMonitor(Thread thrd)
{
    m_thrd = thrd;
}

@Override
public String getName()
{
    return "JMX Controlled App";
}

@Override
public void start()
{
    // TODO: start application here
    System.out.println("remote start called");
}

@Override
public void stop()
{
    // TODO: stop application here
    System.out.println("remote stop called");

    m_thrd.interrupt();
}

public boolean isRunning()
{
    return Thread.currentThread().isAlive();
}

public static void main(String[] args)
{
    try
    {
        System.out.println("JMX started");

        ThreadMonitorMBean monitor = new ThreadMonitor(Thread.currentThread());

        MBeanServer server = ManagementFactory.getPlatformMBeanServer();

        ObjectName name = new ObjectName("com.example:type=ThreadMonitor");

        server.registerMBean(monitor, name);

        while(!Thread.interrupted())
        {
            // loop until interrupted
            System.out.println(".");
            try 
            {
                Thread.sleep(1000);
            } 
            catch(InterruptedException ex) 
            {
                Thread.currentThread().interrupt();
            }
        }
    }
    catch(Exception e)
    {
        e.printStackTrace();
    }
    finally
    {
        // TODO: some final clean up could be here also
        System.out.println("JMX stopped");
    }
}
}

Controllo dell'applicazione:
eseguilo con stop o start come argomento della riga di comando

public class ThreadMonitorConsole
{

public static void main(String[] args)
{
    try
    {   
        // connecting to JMX
        System.out.println("Connect to JMX service.");
        JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://:9999/jmxrmi");
        JMXConnector jmxc = JMXConnectorFactory.connect(url, null);
        MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();

        // Construct proxy for the the MBean object
        ObjectName mbeanName = new ObjectName("com.example:type=ThreadMonitor");
        ThreadMonitorMBean mbeanProxy = JMX.newMBeanProxy(mbsc, mbeanName, ThreadMonitorMBean.class, true);

        System.out.println("Connected to: "+mbeanProxy.getName()+", the app is "+(mbeanProxy.isRunning() ? "" : "not ")+"running");

        // parse command line arguments
        if(args[0].equalsIgnoreCase("start"))
        {
            System.out.println("Invoke \"start\" method");
            mbeanProxy.start();
        }
        else if(args[0].equalsIgnoreCase("stop"))
        {
            System.out.println("Invoke \"stop\" method");
            mbeanProxy.stop();
        }

        // clean up and exit
        jmxc.close();
        System.out.println("Done.");    
    }
    catch(Exception e)
    {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}
}


Questo è tutto. :-)


7

Un altro modo: la tua applicazione può aprire un server socet e attendere che le informazioni arrivino. Ad esempio una stringa con una parola "magica" :) e poi reagire per fare shutdown: System.exit (). È possibile inviare tali informazioni a socke utilizzando un'applicazione esterna come telnet.


3

Ecco una soluzione un po 'complicata, ma portatile:

  • Nella tua applicazione implementa un hook di spegnimento
  • Quando si desidera chiudere correttamente la JVM, installare un agente Java che richiama System.exit () utilizzando l' API di collegamento .

Ho implementato l'agente Java. È disponibile su Github: https://github.com/everit-org/javaagent-shutdown

Una descrizione dettagliata della soluzione è disponibile qui: https://everitorg.wordpress.com/2016/06/15/shutting-down-a-jvm-process/


2

Domanda simile qui

I finalizzatori in Java sono pessimi. Aggiungono un sacco di overhead alla raccolta dei rifiuti. Evitalo quando possibile.

ShutdownHook verrà chiamato solo quando la VM si sta spegnendo. Penso che possa fare molto bene quello che vuoi.


1
Quindi shutdownHook viene chiamato in TUTTI i casi? E se "uccidesse" il processo dalla shell?
Ma99uS

Beh, non se gli stai dando un'uccisione -9 :)
Steve g

0

La segnalazione in Linux può essere eseguita con "kill" (man kill per i segnali disponibili), avresti bisogno dell'ID del processo per farlo. (ps ax | grep java) o qualcosa del genere, o memorizza l'id del processo quando il processo viene creato (questo è usato nella maggior parte dei file di avvio di Linux, vedere /etc/init.d)

La segnalazione portatile può essere eseguita integrando un SocketServer nella tua applicazione java. Non è così difficile e ti dà la libertà di inviare qualsiasi comando tu voglia.

Se intendevi infine clausole invece di finalizzatori; non vengono estesi quando viene chiamato System.exit (). I finalizzatori dovrebbero funzionare, ma in realtà non dovrebbero fare nulla di più significativo se non stampare un'istruzione di debug. Sono pericolosi.


0

Grazie per le tue risposte. I ganci di arresto sembrano qualcosa che potrebbe funzionare nel mio caso. Ma mi sono anche imbattuto nella cosa chiamata bean di monitoraggio e gestione:
http://java.sun.com/j2se/1.5.0/docs/guide/management/overview.html
Che offre alcune belle possibilità, per il monitoraggio remoto e la manipolazione del processo java. (È stato introdotto in Java 5)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.