Wie kann ich den Java-Prozess ordnungsgemäß stoppen?

88

Wie stoppe ich einen Java-Prozess unter Linux und Windows ordnungsgemäß?

Wann wird Runtime.getRuntime().addShutdownHookangerufen und wann nicht?

Was ist mit Finalisierern, helfen sie hier?

Kann ich von einer Shell aus ein Signal an einen Java-Prozess senden?

Ich suche vorzugsweise tragbare Lösungen.

Ma99uS
quelle
Sie sollten wirklich definieren, was Sie mit anmutig meinen. (bitte)
Henry B
7
Ich gehe davon aus, dass sie die Möglichkeit erhalten möchten, Ressourcen zu bereinigen, freizugeben, zu sperren und persistente Daten auf die Festplatte zu löschen, bevor das Programm beendet wird.
Steve g

Antworten:

82

Shutdown-Hooks werden in allen Fällen ausgeführt, in denen die VM nicht gewaltsam getötet wird. Wenn Sie also einen "Standard" -Tötung ( SIGTERMüber einen Tötungsbefehl) ausgeben, werden diese ausgeführt. Ebenso werden sie nach dem Aufruf ausgeführt System.exit(int).

Jedoch ein harter Kill ( kill -9oder kill -SIGKILL) dann werden sie nicht ausgeführt. In ähnlicher Weise (und offensichtlich) werden sie nicht ausgeführt, wenn Sie den Computer mit Strom versorgen, ihn in einen Behälter mit kochender Lava fallen lassen oder die CPU mit einem Vorschlaghammer in Stücke schlagen. Das wussten Sie aber wahrscheinlich schon.

Finalizer sollten eigentlich auch laufen, aber es ist am besten, sich beim Bereinigen des Herunterfahrens nicht darauf zu verlassen, sondern sich auf Ihre Shutdown-Hooks zu verlassen, um die Dinge sauber zu stoppen. Und wie immer, seien Sie vorsichtig mit Deadlocks (ich habe gesehen, dass viel zu viele Shutdown-Hooks den gesamten Prozess hängen)!

jsight
quelle
1
Leider scheint dies unter Windows 7 (64 Bit) nicht zu funktionieren. Ich habe versucht, taskill ohne das Force-Flag zu verwenden, und bin auf den folgenden Fehler gestoßen: "FEHLER: Der Prozess mit PID 14324 konnte nicht beendet werden. Grund: Dieser Prozess kann nur mit Gewalt beendet werden (mit der Option / F)." Wenn Sie die Force-Option "/ f" angeben, wird der Vorgang offensichtlich sofort geschlossen.
Jason Huntley
Sie können sich nicht darauf verlassen, dass Finalizer ausgeführt werden.
Thorbjørn Ravn Andersen
47

Ok, nach all den Möglichkeiten, die ich für die Arbeit mit "Java Monitoring and Management" gewählt habe, finden Sie
hier
eine Übersicht, mit der Sie eine Anwendung auf relativ einfache Weise von einer anderen steuern können. Sie können die steuernde Anwendung über ein Skript aufrufen, um die gesteuerte Anwendung ordnungsgemäß zu stoppen, bevor Sie sie beenden.

Hier ist der vereinfachte Code:

Kontrollierte Anwendung:
Führen Sie sie mit den folgenden VM-Parametern aus:
-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");
    }
}
}

Steuern der Anwendung:
Führen Sie sie mit dem Befehl stop oder start als Befehlszeilenargument aus

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


Das ist es. :-)

Ma99uS
quelle
7

Ein anderer Weg: Ihre Anwendung kann einen Server-Socet öffnen und auf die Ankunft einer Information warten. Zum Beispiel eine Zeichenfolge mit einem "magischen" Wort :) und dann reagieren, um herunterzufahren: System.exit (). Sie können solche Informationen mit einer externen Anwendung wie Telnet an den Socke senden.


quelle
3

Hier ist eine etwas knifflige, aber tragbare Lösung:

  • Implementieren Sie in Ihrer Anwendung einen Shutdown-Hook
  • Wenn Sie Ihre JVM ordnungsgemäß herunterfahren möchten, installieren Sie einen Java-Agenten , der System.exit () über die Attach-API aufruft .

Ich habe den Java Agent implementiert. Es ist auf Github verfügbar: https://github.com/everit-org/javaagent-shutdown

Eine detaillierte Beschreibung der Lösung finden Sie hier: https://everitorg.wordpress.com/2016/06/15/shutting-down-a-jvm-process/

Balazs Zsoldos
quelle
2

Ähnliche Frage hier

Finalizer in Java sind schlecht. Sie erhöhen den Aufwand für die Speicherbereinigung erheblich. Vermeiden Sie sie wann immer möglich.

Der shutdownHook wird nur aufgerufen, wenn die VM heruntergefahren wird. Ich denke, es kann sehr gut tun, was Sie wollen.

Steve g
quelle
1
Also wird shutdownHook in ALLEN Fällen aufgerufen? Was ist, wenn der Prozess aus der Shell "beendet" wird?
Ma99uS
Nun, nicht wenn du es tötest -9 :)
Steve g
0

Die Signalisierung unter Linux kann mit "kill" (man kill für die verfügbaren Signale) erfolgen. Dazu benötigen Sie die Prozess-ID. (ps ax | grep java) oder ähnliches oder speichern Sie die Prozess-ID, wenn der Prozess erstellt wird (dies wird in den meisten Linux-Startdateien verwendet, siehe /etc/init.d).

Die tragbare Signalisierung kann durch Integration eines SocketServers in Ihre Java-Anwendung erfolgen. Es ist nicht so schwierig und gibt Ihnen die Freiheit, jeden gewünschten Befehl zu senden.

Wenn Sie schließlich Klauseln anstelle von Finalisierern gemeint haben; Sie werden nicht ausgeführt, wenn System.exit () aufgerufen wird. Finalizer sollten funktionieren, aber eigentlich nichts Wichtigeres tun, als eine Debug-Anweisung zu drucken. Sie sind gefährlich.

Extraneon
quelle
0

Danke für deine Antworten. Shutdown hakt Nähte wie etwas, das in meinem Fall funktionieren würde. Aber ich bin auch auf das Ding namens Monitoring and Management Beans gestoßen:
http://java.sun.com/j2se/1.5.0/docs/guide/management/overview.html
Das bietet einige nette Möglichkeiten für die Fernüberwachung und -manipulation des Java-Prozesses. (Wurde in Java 5 eingeführt)

Ma99uS
quelle