¿Cómo maneja la JVM una excepción lanzada por el método principal?

10

Entiendo las excepciones, arrojándolas, manejándolas y propagándolas a un método más bajo en la pila de llamadas (es decir throws).

Lo que no entiendo es esto:

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

Ahora, supongo que en el caso que mainarroja un Exception, la JVM lo maneja (¿correcto?). Si ese es el caso, entonces mi pregunta es:

¿Cómo maneja la JVM las excepciones generadas main? ¿Qué hace?

Aviv Cohn
fuente

Respuestas:

19

Puede pensar que el public static void mainmétodo en Java o la mainfunción en C es el verdadero punto de entrada de su programa, pero no lo es. Todos los lenguajes de alto nivel (incluido C) tienen un tiempo de ejecución de lenguaje que inicializa el programa y luego transfiere el flujo de control al punto de entrada. En el caso de Java, la inicialización incluirá:

  • configurar la JVM
  • cargar clases requeridas
  • ejecutando bloques de inicializador estático. Esto puede ejecutar código definido por el usuario antes de maininvocarlo. Se supone que estos bloques no arrojan excepciones.

Hay una variedad de formas de implementar el manejo de excepciones, pero para el propósito de esta pregunta, todas pueden verse como un recuadro negro. Sin embargo, lo importante es que el tiempo de ejecución del lenguaje siempre debe proporcionar un controlador de excepciones más externo que capture todas las excepciones que el código de usuario no detecte. Este controlador de excepciones generalmente imprimirá un seguimiento de la pila, cerrará el programa de manera ordenada y saldrá con un código de error. El cierre correcto del programa incluye destruir el gráfico de objetos, invocar finalizadores y liberar recursos como memoria, identificadores de archivos o conexiones de red.

Para fines de ilustración, puede obtener imágenes del tiempo de ejecución envolviendo todo el código en un try-catch gigante que se parece a

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

excepto que no es necesario que un lenguaje realmente ejecute código como este. Se puede implementar la misma semántica en el código throw(o equivalente) que busca el primer controlador de excepciones aplicable.

amon
fuente
9

Todo el código Java se ejecuta en el contexto de un hilo . El JavaDoc vinculado explica el manejo de errores y los criterios de salida, pero aquí está lo esencial:

  • La JVM gira y prepara el entorno de ejecución.
  • La JVM crea un subproceso que ejecutará el main()método utilizando los parámetros de línea de comandos que sean aplicables.
  • La JVM establece un controlador de excepciones no capturado predeterminado que imprime la excepción al error estándar y finaliza.
  • La JVM ejecuta el hilo.

En el caso de una excepción no detectada, el programa efectivamente muere por el tercer elemento anterior. Este comportamiento se especifica con más detalle en la Especificación del lenguaje Java, Sección 11.3


información adicional

Otros han mencionado bloques estáticos y cómo se ejecutan antes main(). Sin embargo, esto requiere un poco más de explicación para entenderlo correctamente.

Al cargar una clase, el cargador de clases debe inicializar todos los static finalestados y ejecutar todos los staticbloques antes de que la clase pueda usarse, para incluir instancias de instancia de la clase (aparte: cree una clase Java donde se inicializa una constante de clase en un bloque estático después de crear un instancia de la clase, y el constructor hace referencia a la constante. ¡Boom!). Sin embargo, todo esto sucede en la lógica del cargador de clases antes de que cualquier código pueda hacer referencia a la clase . Además, la clase se carga en cualquier hilo que haga referencia a la clase.

Esto significa que si la clase que contiene hace main()referencia a otra clase (por ejemplo, constante de clase), esa clase debe cargarse antes de main()ejecutarse para incluir sus bloques estáticos. De lo contrario, los bloques estáticos se ejecutan como anteriormente. Si la clase no se carga, entonces la clase que contiene main()tampoco se cargará y el programa finalizará.

Otro FYI: pueden lanzar bloques estáticos . Errorsson arrojados como están. Exceptionsestán prohibidos (error en tiempo de compilación). RuntimeExceptionsestán envueltos en ExceptionInInitializerError . Estos se manejan según el controlador de excepción no capturado, que normalmente eliminará el subproceso o la aplicación (subproceso principal) a menos que ajuste cuidadosamente la referencia de clase (y la carga) en un try- catch.

Benni
fuente