¿Cómo detener el proceso de Java con gracia?

88

¿Cómo detengo un proceso de Java correctamente en Linux y Windows?

¿Cuándo Runtime.getRuntime().addShutdownHookse llama y cuándo no?

¿Qué pasa con los finalizadores? ¿Ayudan aquí?

¿Puedo enviar algún tipo de señal a un proceso de Java desde un shell?

Busco soluciones preferiblemente portátiles.

Ma99uS
fuente
realmente debería definir lo que quiere decir con gracia. (por favor)
Henry B
7
Supongo que quieren decir que quieren tener la oportunidad de limpiar recursos, liberar, bloquear y vaciar cualquier dato persistente en el disco antes de que se elimine el programa.
Steve g

Respuestas:

82

Los enganches de cierre se ejecutan en todos los casos en los que la máquina virtual no se mata a la fuerza. Entonces, si tuviera que emitir un kill "estándar" ( SIGTERMdesde un comando kill), se ejecutarán. Del mismo modo, se ejecutarán después de llamar System.exit(int).

Sin embargo, una muerte difícil ( kill -9o kill -SIGKILL) no se ejecutará. De manera similar (y obviamente) no se ejecutarán si extrae la energía de la computadora, la deja caer en una tina de lava hirviendo o golpea la CPU en pedazos con un mazo. Sin embargo, probablemente ya lo sabías.

Los finalizadores también deberían ejecutarse, pero es mejor no confiar en eso para la limpieza del apagado, sino confiar en sus ganchos de apagado para detener las cosas de manera limpia. Y, como siempre, tenga cuidado con los interbloqueos (he visto demasiados ganchos de apagado que cuelgan todo el proceso).

jsight
fuente
1
Desafortunadamente, esto no parece funcionar en Windows 7 (64 bits). Intenté usar taskill, sin el indicador de fuerza, y encuentro el siguiente error: "ERROR: El proceso con PID 14324 no se pudo terminar. Razón: este proceso solo se puede terminar de manera forzada (con la opción / F)". Proporcionar la opción de fuerza, "/ f", obviamente cerrará el proceso instantáneamente.
Jason Huntley
No puede confiar en que se ejecuten finalizadores.
Thorbjørn Ravn Andersen
47

Ok, después de todas las posibilidades que he elegido para trabajar con "Monitorización y administración de Java". La
descripción general está aquí.
Esto le permite controlar una aplicación desde otra de una manera relativamente fácil. Puede llamar a la aplicación de control desde un script para detener la aplicación controlada correctamente antes de matarla.

Aquí está el código simplificado:

Aplicación controlada:
ejecútela con los siguientes parámetros de VM:
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port = 9999
-Dcom.sun.management.jmxremote.authenticate = false
-Dcom.sun.management. jmxremote.ssl = falso

//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");
    }
}
}

Aplicación de control:
ejecútela con la parada o inicio como argumento de línea de 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();
    }
}
}


Eso es. :-)

Ma99uS
fuente
7

De otra forma: su aplicación puede abrir un servidor socet y esperar a que llegue una información. Por ejemplo, una cadena con una palabra "mágica" :) y luego reacciona para hacer shutdown: System.exit (). Puede enviar dicha información al socke mediante una aplicación externa como telnet.


fuente
2

Pregunta similar aquí

Los finalizadores en Java son malos. Añaden una gran cantidad de gastos generales a la recolección de basura. Evítelos siempre que sea posible.

El shutdownHook solo se llamará cuando la VM se esté apagando. Creo que muy bien puede hacer lo que quieras.

Steve g
fuente
1
Entonces, ¿se llama a shutdownHook en TODOS los casos? ¿Qué pasa si 'mata' el proceso desde el shell?
Ma99uS
Bueno, no si lo estás matando -9 :)
Steve g
0

La señalización en Linux se puede hacer con "kill" (man kill para las señales disponibles), necesitaría el ID del proceso para hacer eso. (ps ax | grep java) o algo así, o almacene la identificación del proceso cuando se crea el proceso (esto se usa en la mayoría de los archivos de inicio de Linux, consulte /etc/init.d)

La señalización portátil se puede realizar integrando un SocketServer en su aplicación java. No es tan difícil y te da la libertad de enviar cualquier comando que desees.

Si te refieres a cláusulas de final en lugar de finalizadores; no se extienden cuando se llama a System.exit (). Los finalizadores deberían funcionar, pero en realidad no deberían hacer nada más significativo que imprimir una declaración de depuración. Son peligrosos.

extraneón
fuente
0

Gracias por tus respuestas. El cierre cierra las costuras como algo que funcionaría en mi caso. Pero también me encontré con lo que se llama beans de monitoreo y administración:
http://java.sun.com/j2se/1.5.0/docs/guide/management/overview.html
Eso brinda algunas buenas posibilidades, para el monitoreo remoto y la manipulación del proceso java. (Fue introducido en Java 5)

Ma99uS
fuente