Como a JVM lida com uma exceção lançada pelo método principal?

10

Entendo exceções, jogando-as, manipulando-as e propagando-as para um método mais baixo na pilha de chamadas (ou seja throws).

O que eu não entendo é o seguinte:

public static void main(String[] args) throws Exception {
    ...
}

Agora, suponho que, no caso de mainlançar um Exception, a JVM lide com isso (correto?). Se for esse o caso, então minha pergunta é:

Como a JVM lida com exceções geradas main? O que isso faz?

Aviv Cohn
fonte

Respostas:

19

Você pode pensar que o public static void mainmétodo em Java ou a mainfunção em C é o ponto de entrada real do seu programa - mas não é. Todos os idiomas de alto nível (incluindo C) têm um tempo de execução de idioma que inicializa o programa e depois transfere o fluxo de controle para o ponto de entrada. No caso de Java, a inicialização incluirá:

  • configurando a JVM
  • carregando classes obrigatórias
  • executando blocos inicializadores estáticos. Isso pode executar o código definido pelo usuário antes de mainser chamado. Esses blocos não devem lançar exceções.

Existem várias maneiras de implementar o tratamento de exceções, mas, para o propósito desta pergunta, todas elas podem ser vistas como uma caixa preta. O importante, no entanto, é que o tempo de execução do idioma sempre forneça um manipulador de exceção mais externo que captura todas as exceções que não são capturadas pelo código do usuário. Esse manipulador de exceção geralmente imprime um rastreamento de pilha, encerra o programa em uma ordem ordenada e sai com um código de erro. O desligamento adequado do programa inclui destruir o gráfico do objeto, chamar finalizadores e liberar recursos como memória, identificadores de arquivo ou conexões de rede.

Para fins de ilustração, é possível criar imagens do tempo de execução agrupando todo o código em um try-catch gigante que se parece com

try {
    loadClasses();
    runInitializers();
    main(argv);
    System.exit(0);
} catch (Throwable e) {
    e.printStackTrace();
    System.exit(-1);
}

exceto que não é necessário que um idioma execute código como esse. A mesma semântica pode ser implementada no código throw(ou equivalente) que procura o primeiro manipulador de exceção aplicável.

amon
fonte
9

Todo o código Java é executado no contexto de um encadeamento . O JavaDoc vinculado explica o tratamento de erros e os critérios de saída, mas aqui está a essência:

  • A JVM se gira e prepara o ambiente de execução.
  • A JVM cria um encadeamento que executará o main()método usando quaisquer parâmetros da linha de comandos aplicáveis.
  • A JVM configura um manipulador de exceção não capturado padrão que imprime a exceção em erro padrão e termina.
  • A JVM executa o encadeamento.

No caso de uma exceção não capturada, o programa morre efetivamente pelo terceiro item acima. Esse comportamento é especificado posteriormente na Especificação de Linguagem Java, Seção 11.3


informação adicional

Outros mencionaram blocos estáticos e como eles são executados antes main(). No entanto, isso requer um pouco mais de explicação para entender corretamente.

Ao carregar uma classe, o carregador de classes deve inicializar todos os static finalestados e executar todos os staticblocos antes que a classe possa ser usada, para incluir instâncias da classe (além disso: crie uma classe Java em que uma constante de classe seja inicializada em um bloco estático após a criação de um instância da classe e o construtor referencia a constante. Boom!). No entanto, tudo isso acontece na lógica do carregador de classes antes que qualquer código possa fazer referência à classe . Além disso, a classe é carregada em qualquer thread que faça referência à classe.

O que isso significa é que, se a classe que contém main()referências a outra classe (por exemplo, constante da classe), essa classe deve ser carregada antes da main()execução para incluir seus blocos estáticos. Caso contrário, os blocos estáticos são executados como acima. Se a classe falhar ao carregar, a classe que contém main()também falhará ao carregar e o programa será encerrado.

Outro FYI: blocos estáticos podem ser lançados. Errorssão jogados como estão. Exceptionssão proibidos (erro em tempo de compilação). RuntimeExceptionssão agrupados em ExceptionInInitializerError . Eles são manipulados pelo manipulador de exceção não capturado, que normalmente mata o thread ou o aplicativo (thread principal), a menos que você agrupe cuidadosamente a referência de classe (e o carregamento) em um try- catch.

Benni
fonte