Comment arrêter le processus Java avec élégance?

88

Comment arrêter un processus Java en douceur sous Linux et Windows?

Quand est- Runtime.getRuntime().addShutdownHookil appelé et quand n'est-ce pas?

Et les finaliseurs, aident-ils ici?

Puis-je envoyer une sorte de signal à un processus Java à partir d'un shell?

Je recherche des solutions de préférence portables.

Ma99uS
la source
vous devez vraiment définir ce que vous entendez par grâce. (s'il vous plaît)
Henry B
7
Je suppose qu'ils veulent dire qu'ils veulent avoir la possibilité de nettoyer une ressource, de libérer, de verrouiller et de vider toutes les données persistantes sur le disque avant que le programme ne soit tué.
Steve g

Réponses:

82

Les hooks d'arrêt s'exécutent dans tous les cas où la machine virtuelle n'est pas tuée de force. Donc, si vous deviez lancer un kill "standard" (à SIGTERMpartir d'une commande kill) alors ils s'exécuteront. De même, ils s'exécuteront après avoir appelé System.exit(int).

Cependant un hard kill ( kill -9ou kill -SIGKILL) alors ils ne s'exécuteront pas. De même (et évidemment), ils ne s'exécuteront pas si vous tirez l'alimentation de l'ordinateur, le déposez dans une cuve de lave bouillante ou si vous battez le processeur en morceaux avec un marteau. Vous le saviez probablement déjà.

Les finaliseurs devraient vraiment fonctionner aussi, mais il est préférable de ne pas compter sur cela pour le nettoyage d'arrêt, mais plutôt sur vos crochets d'arrêt pour arrêter les choses proprement. Et, comme toujours, faites attention aux blocages (j'ai vu beaucoup trop de crochets d'arrêt suspendre tout le processus)!

jsight
la source
1
Malheureusement, cela ne semble pas fonctionner sous Windows 7 (64 bits). J'ai essayé d'utiliser taskill, sans l'indicateur de force, et j'ai rencontré l'erreur suivante: "ERREUR: Le processus avec le PID 14324 n'a pas pu être terminé. Raison: Ce processus ne peut être interrompu que de force (avec l'option / F)." Fournir l'option de force, "/ f", fermera évidemment le processus instantanément.
Jason Huntley
Vous ne pouvez pas compter sur les finaliseurs en cours d'exécution.
Thorbjørn Ravn Andersen
47

Ok, après toutes les possibilités que j'ai choisies pour travailler avec "Java Monitoring and Management" La
vue d'ensemble est ici
Cela vous permet de contrôler une application à partir d'une autre de manière relativement simple. Vous pouvez appeler l'application de contrôle à partir d'un script pour arrêter l'application contrôlée correctement avant de la tuer.

Voici le code simplifié:

Application contrôlée:
exécutez-la avec les paramètres de VM suivants:
-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");
    }
}
}

Contrôle de l'application:
exécutez-le avec l' arrêt ou le démarrage comme argument de ligne de commande

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


C'est tout. :-)

Ma99uS
la source
7

Une autre manière: votre application peut ouvrir un socet de serveur et attendre qu'une information lui soit arrivée. Par exemple une chaîne avec un mot "magique" :) et ensuite réagir pour faire shutdown: System.exit (). Vous pouvez envoyer ces informations à la chaussette en utilisant une application externe comme telnet.


la source
3

Voici une solution un peu délicate, mais portable:

  • Dans votre application, implémentez un hook d'arrêt
  • Lorsque vous souhaitez arrêter correctement votre JVM, installez un agent Java qui appelle System.exit () à l'aide de l' API Attach .

J'ai implémenté l'agent Java. Il est disponible sur Github: https://github.com/everit-org/javaagent-shutdown

Une description détaillée de la solution est disponible ici: https://everitorg.wordpress.com/2016/06/15/shutting-down-a-jvm-process/

Balazs Zsoldos
la source
2

Question similaire ici

Les finaliseurs en Java sont mauvais. Ils ajoutent beaucoup de frais généraux au ramassage des ordures. Évitez-les autant que possible.

Le shutdownHook n'est appelé que lorsque la machine virtuelle s'arrête. Je pense qu'il peut très bien faire ce que vous voulez.

Steve g
la source
1
Donc shutdownHook est appelé dans TOUS les cas? Et si tu «tuais» le processus depuis le shell?
Ma99uS
Eh bien, pas si vous lui donnez un kill -9 :)
Steve g
0

La signalisation sous Linux peut être faite avec "kill" (man kill pour les signaux disponibles), vous aurez besoin de l'ID de processus pour le faire. (ps ax | grep java) ou quelque chose comme ça, ou stockez l'ID du processus lorsque le processus est créé (ceci est utilisé dans la plupart des fichiers de démarrage Linux, voir /etc/init.d)

La signalisation portable peut être effectuée en intégrant un SocketServer dans votre application java. Ce n'est pas si difficile et vous donne la liberté d'envoyer n'importe quelle commande que vous voulez.

Si vous vouliez dire enfin des clauses au lieu de finaliseurs; ils ne sont pas exécutés lorsque System.exit () est appelé. Les finaliseurs devraient fonctionner, mais ne devraient rien faire de plus significatif que d'imprimer une instruction de débogage. Ils sont dangereux.

extraneon
la source
0

Merci pour vos réponses. Shutdown crochets coutures comme quelque chose qui fonctionnerait dans mon cas. Mais je suis aussi tombé sur ce qu'on appelle les beans de surveillance et de gestion:
http://java.sun.com/j2se/1.5.0/docs/guide/management/overview.html
Cela donne de belles possibilités, pour la surveillance et la manipulation à distance du processus java. (A été introduit dans Java 5)

Ma99uS
la source