Quando devemos chamar System.exit em Java

193

Em Java, qual é a diferença com ou sem o System.exit(0)seguinte código?

public class TestExit
{      
    public static void main(String[] args)
    { 
        System.out.println("hello world");

        System.exit(0);  // is it necessary? And when it must be called? 
    }      
}

O documento diz: "Este método nunca retorna normalmente". O que isso significa?

pierrotlefou
fonte

Respostas:

207

System.exit()pode ser usado para executar ganchos de desligamento antes do encerramento do programa. Essa é uma maneira conveniente de lidar com o desligamento em programas maiores, onde todas as partes do programa não podem (e não deveriam) estar cientes uma da outra. Então, se alguém quiser sair, ele pode simplesmente ligarSystem.exit() e os ganchos de desligamento (se configurados corretamente) cuidam de realizar todas as cerimônias de desligamento necessárias, como fechar arquivos, liberar recursos etc.

"Este método nunca retorna normalmente." significa apenas que o método não retornará; uma vez que um thread chegue lá, ele não voltará.

Outra maneira, talvez mais comum, de sair de um programa é simplesmente chegar ao final do mainmétodo. Porém, se houver algum encadeamento que não seja daemon em execução, eles não serão encerrados e, portanto, a JVM não sairá. Portanto, se você tiver algum desses threads não daemon, precisará de outros meios (além dos ganchos de encerramento) para desligar todos os threads não daemon e liberar outros recursos. Se não houver outros encadeamentos que não sejam daemon, retornar de mainencerrará a JVM e chamará os ganchos de encerramento.

Por alguma razão, os ganchos de desligamento parecem ser um mecanismo subvalorizado e incompreendido, e as pessoas estão reinventando a roda com todos os tipos de hacks personalizados proprietários para encerrar seus programas. Eu recomendaria o uso de ganchos de desligamento; está tudo no Runtime padrão que você usará de qualquer maneira.

Joonas Pulakka
fonte
10
"Este método nunca retorna normalmente." significa apenas que o método não retornará; uma vez que um thread chegue lá, ele não voltará. Nota, em particular, que isso significa que você não pode teste de unidade de um método que faz uma (0) chamada System.exit ...
Bill Michell
5
-1 Incorreto. Os ganchos de encerramento serão executados se a JVM terminar normalmente, não importa se é por causa do System.exit ou da finalização do main (). Veja zx81 / doku / java / javadoc / j2se1.5.0 / docs / api / java / lang /…
sleske
31
@sleske: O término de main () não é suficiente se houver outros threads que não sejam daemon. O desligamento é iniciado somente após o término do último encadeamento que não é daemon, a menos que você chame explicitamente System.exit (). Isso está claramente indicado na documentação do Runtime.
Joonas Pulakka
3
Observe que, se o gancho de desligamento, por sua vez, depender do encadeamento chamado System.exit, haverá um impasse.
djechlin
8
Algo a acrescentar, se alguém pára um tempo de execução, ganchos de desligamento não será executado ( Runtime.getRuntime().halt())
Vampira
50

Nesse caso, não é necessário. Nenhum encadeamento extra será iniciado, você não está alterando o código de saída (o padrão é 0) - basicamente é inútil.

Quando os documentos dizem que o método nunca retorna normalmente, significa que a linha de código subsequente é efetivamente inacessível, mesmo que o compilador não saiba que:

System.exit(0);
System.out.println("This line will never be reached");

Uma exceção será lançada ou a VM será encerrada antes de retornar. Nunca "apenas retornará".

É muito raro valer a pena chamar System.exit()IME. Pode fazer sentido se você estiver escrevendo uma ferramenta de linha de comando e desejar indicar um erro através do código de saída, em vez de apenas lançar uma exceção ... mas não me lembro da última vez em que o usei no código de produção normal .

Jon Skeet
fonte
2
Por que o compilador não sabe disso? System.exit () não é especial o suficiente para garantir código de detecção específico?
Bart van Heukelom
3
@ Bart: Não, acho que não. Colocar casos especiais no idioma para coisas assim aumenta a complexidade do idioma, com muito pouco benefício.
quer
Eu pensei que "nunca retorna normalmente" tinha a ver com "conclusão abrupta" da declaração. (Seção 14.1 do JLS). Estou errado?
Aioobe
@aioobe: Não, você está certo. System.exit()nunca será concluído normalmente - sempre será concluído com uma exceção ou com o encerramento da VM (que não é realmente coberto pelo JLS).
Jon Skeet
1
@piechuckerr: O que faz você pensar isso? É perfeitamente razoável chamá-lo com um valor diferente de 0, para indicar ao shell de chamada (ou qualquer outra coisa) que o programa encontrou um erro.
Jon Skeet
15

O método nunca retorna porque é o fim do mundo e nenhum do seu código será executado em seguida.

Seu aplicativo, no seu exemplo, sairia de qualquer maneira no mesmo local no código, mas, se você usar System.exit. você tem a opção de retornar um código personalizado para o ambiente, como, por exemplo,

System.exit(42);

Quem fará uso do seu código de saída? Um script que chamou o aplicativo. Funciona em Windows, Unix e todos os outros ambientes com scripts.

Por que retornar um código? Para dizer coisas como "não obtive sucesso", "o banco de dados não respondeu".

Para ver como obter o valor de um código de saída e usá-lo em um script shell unix ou script cmd do windows, verifique esta resposta neste site

mico
fonte
14

System.exit(0)finaliza a JVM. Em exemplos simples como este, é difícil perceber a diferença. O parâmetro é passado de volta ao sistema operacional e é normalmente usado para indicar finalização anormal (por exemplo, algum tipo de erro fatal); portanto, se você chamar java a partir de um arquivo em lotes ou script de shell, poderá obter esse valor e ter uma ideia se o aplicativo foi bem-sucedido.

Isso causaria um grande impacto se você chamasse System.exit(0)um aplicativo implantado em um servidor de aplicativos (pense nisso antes de tentar).

Qwerky
fonte
12

Em aplicativos que podem ter ganchos de desligamento complexos, esse método não deve ser chamado de um thread desconhecido. System.exitnunca sai normalmente porque a chamada será bloqueada até que a JVM seja encerrada. É como se qualquer código em execução tivesse o plugue de força pressionado antes de terminar. A chamada System.exitiniciará os ganchos de desligamento do programa e qualquer segmento que as chamadas System.exitbloquearão até o encerramento do programa. Isso implica que, se o gancho de encerramento, por sua vez, enviar uma tarefa para o encadeamento do qual System.exitfoi chamado, o programa entrará em conflito.

Estou lidando com isso no meu código com o seguinte:

public static void exit(final int status) {
    new Thread("App-exit") {
        @Override
        public void run() {
            System.exit(status);
        }
    }.start();
}
djechlin
fonte
Eu também tive o problema em que o System.exit na verdade não terminava a JVM. Apenas aconteceu sob certas condições. Um threadump que eu fiz não me deu nenhuma idéia sobre quais threads / manipuladores de desligamento estão bloqueando o desligamento. Usar o código descrito no comentário me ajudou a resolvê-lo!
Kai
7

Embora a resposta tenha sido realmente útil, mas de alguma forma faltou mais alguns detalhes extras. Espero que abaixo ajude a entender o processo de desligamento em java, além da resposta acima:

  1. Em um desligamento ordenado *, a JVM inicia primeiro todos os ganchos de desligamento registrados. Ganchos de desligamento são threads não iniciados registrados com Runtime.addShutdownHook.
  2. A JVM não garante a ordem na qual os ganchos de desligamento são iniciados. Se algum encadeamento de aplicativo (daemon ou não daemon) ainda estiver em execução no momento do desligamento, eles continuarão sendo executados simultaneamente com o processo de desligamento .
  3. Quando todos os ganchos de desligamento forem concluídos, a JVM poderá optar por executar finalizadores se runFinalizersOnExit for verdadeiro e, em seguida, parar.
  4. A JVM não tenta parar ou interromper nenhum encadeamento de aplicativo que ainda esteja em execução no momento do encerramento; eles são encerrados abruptamente quando a JVM finalmente pára.
  5. Se os ganchos ou finalizadores de encerramento não forem concluídos, o processo de encerramento ordenado "trava" e a JVM deve ser encerrada abruptamente.
  6. Em um encerramento abrupto, a JVM não precisa fazer nada além de interromper a JVM; ganchos de desligamento não serão executados.

PS: A JVM pode desligar de forma ordenada ou abrupta .

  1. Um encerramento ordenado é iniciado quando o último encadeamento "normal" (não daemon) termina, alguém chama System.exit ou por outros meios específicos da plataforma (como enviar um SIGINT ou pressionar Ctrl-C).
  2. Embora acima seja a maneira padrão e preferencial de desligar a JVM, ela também pode ser desligada abruptamente chamando Runtime.halt ou eliminando o processo da JVM através do sistema operacional (como o envio de um SIGKILL).
John
fonte
7

NUNCA se deve chamar System.exit(0)por estas razões:

  1. É um "goto" oculto e "gotos" interrompem o fluxo de controle. Contar com ganchos nesse contexto é um mapeamento mental que todo desenvolvedor da equipe precisa conhecer.
  2. Sair do programa "normalmente" fornece o mesmo código de saída para o sistema operacional, System.exit(0)por isso é redundante.

    Se o seu programa não pode sair "normalmente", você perdeu o controle do seu desenvolvimento [design]. Você sempre deve ter controle total do estado do sistema.

  3. Problemas de programação, como a execução de threads que não são parados, normalmente ficam ocultos.
  4. Você pode encontrar um estado inconsistente do aplicativo interrompendo os threads de forma anormal. (Consulte o item 3)

A propósito: Retornar outros códigos de retorno diferentes de 0 faz sentido se você deseja indicar o encerramento anormal do programa.

oopexpert
fonte
2
"Você deve sempre ter controle total do estado do sistema." Isso simplesmente não é possível em Java. As estruturas de IoC e os servidores de aplicativos são apenas duas maneiras muito populares e amplamente usadas para você abrir mão do controle do estado do sistema. E isso é totalmente bom.
Mhlz 29/09/2015
Por favor, execute System.exit (0) no seu servidor de aplicativos e conte-me sobre as reações do administrador do servidor ... Você perdeu o assunto desta pergunta e minha resposta.
Oopexpert
Talvez eu não tenha sido claro o suficiente. Você sempre deve ter controle total do estado do sistema pelo qual é responsável. Sim, algumas responsabilidades foram transferidas para servidores de aplicativos e IoC. Mas eu não estava falando sobre isso.
Oopexpert
5

System.exit é necessário

  • quando você deseja retornar um código de erro diferente de 0
  • quando você deseja sair do seu programa de algum lugar que não seja main ()

No seu caso, faz exatamente a mesma coisa que o simples retorno do main.

mfx
fonte
O que você quer dizer com este último? A maneira correta de desligar o programa é interromper todos os threads (agradavelmente), uma mudança que não precisa ser iniciada a partir do thread principal, portanto não exité a sua única opção.
Bart van Heukelom
@ Bart: o que você descreve é uma das maneiras possíveis de desligar um programa, e não da maneira correta . Não há nada errado em usar ganchos de desligamento para interromper bem todos os threads. E ganchos de desligamento são lançados com exit. Não é a única opção, mas definitivamente uma opção que vale a pena considerar.
Joonas Pulakka
Mas o que eu deduzo dos documentos, os ganchos de desligamento são muito delicados e podem não ter muito tempo para fazer o que você deseja que eles façam. De qualquer maneira, use-os para sair bem no caso de um desligamento do sistema operacional ou algo assim, mas se você for chamar System.exit (), acho melhor fazer o código de desligamento antes dele (após o qual você provavelmente não precisa System.exit () (mais))
Bart van Heukelom
1
Os ganchos de desligamento têm todo o tempo que levam para terminar. A VM não é interrompida antes que todos os ganchos retornem. É realmente possível impedir que a VM saia registrando um gancho de desligamento que leva muito tempo para ser executado. Mas é claro que é possível fazer a sequência de desligamento de várias maneiras alternativas.
Joonas Pulakka
Retornar do main não será encerrado se houver outros threads que não sejam daemon.
djechlin
4

A especificação da linguagem Java diz que

Saída do Programa

Um programa termina toda a sua atividade e sai quando uma das duas coisas acontece:

Todos os threads que não são daemon são encerrados.

Algum encadeamento chama o método de saída da classe Runtime ou classe System , e a operação de saída não é proibida pelo gerenciador de segurança.

Isso significa que você deve usá-lo quando tiver um programa grande (bem, pelo menos maior que este) e quiser concluir sua execução.

Marcin Szymczak
fonte
3

Se você tiver outro programa em execução na JVM e usar o System.exit, esse segundo programa também será fechado. Imagine, por exemplo, que você execute uma tarefa java em um nó do cluster e que o programa java que gerencia o nó do cluster seja executado na mesma JVM. Se o trabalho usasse System.exit, ele não apenas sairia do trabalho, mas também "desligaria o nó completo". Você não poderá enviar outro trabalho para esse nó do cluster, pois o programa de gerenciamento foi fechado acidentalmente.

Portanto, não use System.exit se desejar controlar seu programa de outro programa java na mesma JVM.

Use System.exit se desejar fechar a JVM completa de propósito e se desejar tirar proveito das possibilidades descritas nas outras respostas (por exemplo, ganchos de desligamento: gancho de desligamento de Java , valor de retorno diferente de zero para a linha de comandos chama: Como obter o status de saída de um programa Java no arquivo em lotes do Windows ).

Também dê uma olhada em Exceções de tempo de execução: System.exit (num) ou ative uma RuntimeException de main?

Stefan
fonte