web-dev-qa-db-fra.com

Comment arrêter Java processus correctement?

Comment puis-je arrêter un processus Java dans Linux et Windows?)

Quand est-ce que Runtime.getRuntime().addShutdownHook est appelé et quand ne le fait-il pas?

Qu'en est-il des finaliseurs, aident-ils ici?

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

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

85
Ma99uS

Les crochets d'arrêt s'exécutent dans tous les cas où le VM n'est pas tué de force. Donc, si vous devez émettre un kill "standard" (SIGTERM à partir d'une commande de kill), ils seront alors De même, ils s'exécuteront après avoir appelé System.exit(int).

Cependant, une tuerie dure (kill -9 ou kill -SIGKILL) alors ils ne seront pas exécutés. De même (et évidemment), ils ne fonctionneront pas si vous retirez l'alimentation de l'ordinateur, si vous le déposez dans une cuve de lave en ébullition ou si vous battez le processeur en plusieurs morceaux avec un marteau pilon. Vous le saviez probablement déjà, cependant.

Les finaliseurs devraient vraiment fonctionner aussi bien, mais il vaut mieux ne pas compter sur cela pour le nettoyage après arrêt, mais plutôt sur vos crochets d'arrêt pour arrêter les choses proprement. Et, comme toujours, faites attention aux impasses (j'ai vu beaucoup trop de crochets d'arrêt pendant tout le processus)!

79
jsight

Ok, après toutes les possibilités que j'ai choisies de travailler avec "Java Monitoring and Management"
la vue d'ensemble est ici
Cela vous permet de contrôler une application 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 avant de la tuer.

Voici le code simplifié:

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

Application de contrôle:
l'exécutez avec le stop ou start 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 ça. :-)

47
Ma99uS

Une autre façon: votre application peut ouvrir un socet de serveur et attendre qu'une information lui soit parvenue. Par exemple, une chaîne avec un mot "magique" :) et réagit pour faire shutdown: System.exit (). Vous pouvez envoyer ces informations à la chaussette à l'aide d'une application externe telle que Telnet.

7
Ermak

Question similaire ici

Les finaliseurs dans Java sont incorrects. Ils alourdissent considérablement le ramassage des ordures. Évitez-les autant que possible.

ShutdownHook ne sera appelé que lorsque VM est en train de s’arrêter. Je pense qu’il peut très bien faire ce que vous voulez.

2
Steve g

Voici une solution un peu délicate mais portable:

  • Dans votre application, implémentez un crochet d'arrêt
  • Lorsque vous souhaitez éteindre votre machine virtuelle Java avec élégance, installez un agent Java qui appelle System.exit () à l'aide de la commande - Attach API .

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/

2
Balazs Zsoldos

Merci pour vos réponses. La fermeture des crochets ressemble à quelque chose qui fonctionnerait dans mon cas. Mais je suis aussi tombé sur ce qu'on appelle les beans Monitoring and Management:
http://Java.Sun.com/j2se/1.5.0/docs/guide/management/overview.html
Cela donne quelques possibilités intéressantes pour la surveillance à distance et la manipulation du processus Java. (A été introduit dans Java 5)

0
Ma99uS

La signalisation sous Linux peut être effectuée 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 stocke l'identifiant du processus lorsque le processus est créé (utilisé dans la plupart des fichiers de démarrage de 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 toute commande de votre choix.

Si vous vouliez enfin parler de 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 important que d’imprimer une déclaration de débogage. Ils sont dangereux.

0
extraneon