Como parar o processo java normalmente?

88

Como interrompo um processo Java normalmente no Linux e no Windows?

Quando é Runtime.getRuntime().addShutdownHookchamado e quando não é?

E os finalizadores, eles ajudam aqui?

Posso enviar algum tipo de sinal para um processo Java de um shell?

Procuro soluções preferencialmente portáteis.

Ma99uS
fonte
você realmente deve definir o que entende por graciosamente. (por favor)
Henry B
7
Suponho que eles significam que desejam ter a chance de limpar recursos, liberar, bloquear e liberar quaisquer dados persistentes para o disco antes que o programa seja encerrado.
Steve g

Respostas:

82

Os ganchos de desligamento são executados em todos os casos em que a VM não é eliminada à força. Portanto, se você emitir um kill "padrão" ( SIGTERMde um comando kill), eles serão executados. Da mesma forma, eles serão executados após a chamada System.exit(int).

No entanto, uma morte difícil ( kill -9ou kill -SIGKILL), então eles não serão executados. Da mesma forma (e obviamente) eles não serão executados se você puxar a energia do computador, jogá-lo em um tanque de lava fervente ou quebrar a CPU com uma marreta. Você provavelmente já sabia disso, no entanto.

Os finalizadores também devem ser executados, mas é melhor não depender disso para a limpeza de desligamento, mas sim de seus ganchos de desligamento para interromper as coisas de forma limpa. E, como sempre, tome cuidado com os bloqueios (já vi muitos ganchos de desligamento travar todo o processo)!

jsight
fonte
1
Infelizmente, isso não parece funcionar no Windows 7 (64 bits). Tentei usar o taskill, sem o sinalizador de força, e encontrei o seguinte erro: "ERRO: O processo com PID 14324 não pôde ser encerrado. Motivo: Este processo só pode ser encerrado à força (com a opção / F)." Fornecer a opção de força, "/ f", obviamente fechará o processo instantaneamente.
Jason Huntley
Você não pode contar com a execução de finalizadores.
Thorbjørn Ravn Andersen
47

Ok, depois de todas as possibilidades que escolhi para trabalhar com "Monitoramento e gerenciamento Java", a
Visão geral está aqui.
Isso permite que você controle um aplicativo de outro de maneira relativamente fácil. Você pode chamar o aplicativo de controle de um script para interromper o aplicativo controlado normalmente antes de eliminá-lo.

Aqui está o código simplificado:

Aplicativo controlado:
execute-o com os seguintes parâmetros da 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");
    }
}
}

Aplicativo de controle:
execute-o com stop ou start como o argumento da linha 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();
    }
}
}


É isso aí. :-)

Ma99uS
fonte
7

Outra maneira: seu aplicativo pode abrir um servidor de rede e esperar que uma informação chegue a ele. Por exemplo, uma string com uma palavra "mágica" :) e, em seguida, reagir para desligar: System.exit (). Você pode enviar essas informações para o socke usando um aplicativo externo como o telnet.


fonte
3

Aqui está uma solução um pouco complicada, mas portátil:

  • Em seu aplicativo, implemente um gancho de desligamento
  • Quando você deseja encerrar sua JVM normalmente, instale um Agente Java que chama System.exit () usando a API Attach .

Implementei o Agente Java. Ele está disponível no Github: https://github.com/everit-org/javaagent-shutdown

A descrição detalhada sobre a solução está disponível aqui: https://everitorg.wordpress.com/2016/06/15/shutting-down-a-jvm-process/

Balazs Zsoldos
fonte
2

Pergunta semelhante aqui

Finalizadores em Java são ruins. Eles adicionam muita sobrecarga à coleta de lixo. Evite-os sempre que possível.

O shutdownHook só será chamado quando a VM estiver sendo desligada. Acho que muito bem pode fazer o que quiser.

Steve g
fonte
1
Então shutdownHook é chamado em TODOS os casos? E se 'matar' o processo do shell?
Ma99uS,
Bem, não se você estiver tentando matar -9 :)
Steve g
0

A sinalização no Linux pode ser feita com "kill" (man kill para os sinais disponíveis), você precisa do ID do processo para fazer isso. (ps ax | grep java) ou algo parecido, ou armazene o id do processo quando o processo for criado (isso é usado na maioria dos arquivos de inicialização do Linux, consulte /etc/init.d)

A sinalização portátil pode ser feita integrando um SocketServer em seu aplicativo java. Não é tão difícil e dá a você a liberdade de enviar qualquer comando que desejar.

Se você quis dizer finalmente cláusulas em vez de finalizadores; eles não são extintos quando System.exit () é chamado. Os finalizadores devem funcionar, mas não devem fazer nada mais significativo a não ser imprimir uma instrução de depuração. Eles são perigosos.

extrânon
fonte
0

Obrigado por suas respostas. Os ganchos de desligamento parecem algo que funcionaria no meu caso. Mas também me deparei com a coisa chamada beans de monitoramento e gerenciamento:
http://java.sun.com/j2se/1.5.0/docs/guide/management/overview.html
Isso oferece algumas possibilidades interessantes, para monitoramento remoto e manipulação do processo java. (Foi introduzido em Java 5)

Ma99uS
fonte