Como lidar com o sinal SIGKILL em Java?

Como você lida com a limpeza quando o programa recebe um sinal de morte?

Por exemplo, há um aplicativo ao qual me conecto que deseja que qualquer aplicativo de terceiros (meu aplicativo) envie um comando de finish ao fazer logout. Qual é a melhor palavra para enviar o comando finish quando meu aplicativo foi destruído com um kill -9 ?

edit 1: kill -9 não pode ser capturado. Obrigado pessoal por me corrigir.

Editar 2: Eu acho que este caso seria quando o chama apenas matar o que é o mesmo que ctrl-c

   

A maneira de lidar com isso com algo diferente de kill -9 seria registrar um gancho de desligamento . Se você puder usar ( SIGTERM ), kill -15 o gancho de desligamento funcionará. ( SIGINT ) kill -2 faz com que o programa saia e execute os ganchos de desligamento.

Registra um novo gancho de desligamento da máquina virtual.

A máquina virtual Java é encerrada em resposta a dois tipos de events:

 * The program exits normally, when the last non-daemon thread exits or 

quando o método de saída (equivalentemente, System.exit) é invocado ou

 * The virtual machine is terminated in response to a user 

interromper, como digitar ^ C ou um evento de todo o sistema, como logoff do usuário ou desligamento do sistema.

Eu tentei o seguinte programa de teste no OSX 10.6.3 e no kill -9 ele NÃO executou o gancho de desligamento, não achei que fosse. Em um kill -15 ele executa o gancho de desligamento toda vez.

 public class TestShutdownHook { public static void main(final String[] args) throws InterruptedException { Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { System.out.println("Shutdown hook ran!"); } }); while (true) { Thread.sleep(1000); } } } 

Não há como realmente lidar com um kill -9 em qualquer programa.

Em raras circunstâncias, a máquina virtual pode interromper, ou seja, parar de funcionar sem desligar corretamente. Isso ocorre quando a máquina virtual é terminada externamente, por exemplo, com o sinal SIGKILL no Unix ou a chamada TerminateProcess no Microsoft Windows.

A única opção real para lidar com um kill -9 é fazer com que outro programa de observador assista ao desaparecimento de seu programa principal ou use um script de wrapper. Você poderia fazer isso com um script de shell que pesquisou o comando ps procurando por seu programa na lista e agir de acordo quando ele desapareceu.

 #!/usr/bin/env bash java TestShutdownHook wait # notify your other app that you quit echo "TestShutdownHook quit" 

Existem maneiras de lidar com seus próprios sinais em determinadas JVMs – consulte este artigo sobre a JVM HotSpot, por exemplo.

Usando a chamada de método sun.misc.Signal.handle(Signal, SignalHandler) do Sun sun.misc.Signal.handle(Signal, SignalHandler) você também pode registrar um manipulador de sinal, mas provavelmente não para sinais como INT ou TERM , pois são usados ​​pela JVM.

Para poder manipular qualquer sinal, você teria que sair da JVM e entrar no território do sistema operacional.

O que eu geralmente faço para (por exemplo) detectar terminações anormais é lançar minha JVM dentro de um script Perl, mas ter o script esperando pela JVM usando a chamada do sistema waitpid .

Então, sou informado sempre que a JVM sai e por que ela saiu e posso tomar as providências necessárias.

Você pode usar Runtime.getRuntime().addShutdownHook(...) , mas não pode ser garantido que ele será chamado em qualquer caso .

Eu esperaria que a JVM interrompa graciosamente ( thread.interrupt() ) todos os threads em execução criados pelo aplicativo, pelo menos para os sinais SIGINT (kill -2) e SIGTERM (kill -15) .

Dessa forma, o sinal será encaminhado para eles, permitindo um cancelamento de thread e finalização de resources nas formas padrão .

Mas este não é o caso (pelo menos na minha implementação da JVM: Java(TM) SE Runtime Environment (build 1.8.0_25-b17), Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode) .

Como outros usuários comentaram, o uso de ganchos de desligamento parece obrigatório.

Então, como eu lidaria com isso?

Bem, em primeiro lugar, eu não me importo com isso em todos os programas, apenas naqueles onde eu quero manter o controle de cancelamentos de usuários e fins inesperados. Por exemplo, imagine que seu programa java seja um processo gerenciado por outro. Você pode querer diferenciar se ele foi finalizado corretamente ( SIGTERM do processo do gerenciador) ou se ocorreu um desligamento (para reativar automaticamente a tarefa na boot).

Como base, sempre faço meus threads de longa duração periodicamente conscientes do status interrompido e faço uma InterruptedException se eles forem interrompidos. Isso permite a finalização da execução de maneira controlada pelo desenvolvedor (também produzindo o mesmo resultado que as operações padrão de bloqueio). Em seguida, no nível superior da pilha de encadeamentos, InterruptedException é capturado e a limpeza apropriada é executada. Esses encadeamentos são codificados para saber como responder a uma solicitação de interrupção. Design de alta coesão .

Então, nesses casos, eu adiciono um gancho de desligamento, que faz o que eu acho que a JVM deve fazer por padrão: interromper todos os threads não-daemon criados pelo meu aplicativo que ainda estão em execução:

 Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { System.out.println("Interrupting threads"); Set runningThreads = Thread.getAllStackTraces().keySet(); for (Thread th : runningThreads) { if (th != Thread.currentThread() && !th.isDaemon() && th.getClass().getName().startsWith("org.brutusin")) { System.out.println("Interrupting '" + th.getClass() + "' termination"); th.interrupt(); } } for (Thread th : runningThreads) { try { if (th != Thread.currentThread() && !th.isDaemon() && th.isInterrupted()) { System.out.println("Waiting '" + th.getName() + "' termination"); th.join(); } } catch (InterruptedException ex) { System.out.println("Shutdown interrupted"); } } System.out.println("Shutdown finished"); } }); 

Aplicativo de teste completo no github: https://github.com/idelvall/kill-test

Existe uma maneira de reagir a um kill -9: isto é, ter um processo separado que monitora o processo que está sendo morto e limpa depois dele, se necessário. Isso provavelmente envolveria o IPC e seria um pouco trabalhoso, e você ainda pode substituí-lo matando ambos os processos ao mesmo tempo. Eu assumo que não valerá a pena na maioria dos casos.

Quem quer que mate um processo com -9 deve, teoricamente, saber o que está fazendo e deixar as coisas em um estado inconsistente.