¿Cuándo deberíamos llamar a System.exit en Java?

193

En Java, ¿cuál es la diferencia con o sin el System.exit(0)siguiente 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? 
    }      
}

El documento dice: "Este método nunca regresa normalmente". Qué significa eso?

pierrotlefou
fuente

Respuestas:

207

System.exit()se puede usar para ejecutar ganchos de apagado antes de que se cierre el programa. Esta es una forma conveniente de manejar el apagado en programas más grandes, donde todas las partes del programa no pueden (y no deben) ser conscientes entre sí. Luego, si alguien quiere renunciar, simplemente puede llamarSystem.exit() , y los ganchos de cierre (si están configurados correctamente) se encargan de hacer todas las ceremonias de cierre necesarias, como cerrar archivos, liberar recursos, etc.

"Este método nunca vuelve normalmente". significa solo que el método no volverá; Una vez que un hilo va allí, no volverá.

Otra forma, quizás más común, de salir de un programa es simplemente llegar al final del mainmétodo. Pero si hay subprocesos no daemon en ejecución, no se cerrarán y, por lo tanto, la JVM no se cerrará. Por lo tanto, si tiene hilos no daemon, necesita otros medios (además de los ganchos de cierre) para cerrar todos los hilos no daemon y liberar otros recursos. Si no hay otros subprocesos que no sean daemon, al regresar de maincerrará la JVM y llamará a los ganchos de cierre.

Por alguna razón, los ganchos de cierre parecen ser un mecanismo infravalorado y mal entendido, y la gente está reinventando la rueda con todo tipo de hacks personalizados patentados para abandonar sus programas. Yo recomendaría usar ganchos de apagado; todo está allí en el tiempo de ejecución estándar que usarás de todos modos.

Joonas Pulakka
fuente
10
"Este método nunca vuelve normalmente". significa solo que el método no volverá; Una vez que un hilo va allí, no volverá. Tenga en cuenta en particular que esto significa que no puede probar la unidad de un método que realiza una llamada System.exit (0) ...
Bill Michell
55
-1 incorrecto. Los ganchos de apagado se ejecutarán si la JVM finaliza normalmente, sin importar si es debido a System.exit o la terminación de main (). Ver zx81 / doku / java / javadoc / j2se1.5.0 / docs / api / java / lang / ...
sleske
31
@sleske: La terminación de main () no es suficiente si hay otros hilos que no sean daemon. El apagado se inicia solo después de que el último subproceso que no es daemon ha finalizado, a menos que llame explícitamente a System.exit (). Eso está claramente establecido en la documentación de Runtime.
Joonas Pulakka
3
Tenga en cuenta que si su gancho de apagado a su vez se basa en el hilo que llamó System.exit, se bloqueará.
djechlin
8
Algo para agregar, si alguien detiene un tiempo de ejecución, los ganchos de apagado no se ejecutarán ( Runtime.getRuntime().halt())
Rogue
50

En ese caso, no es necesario. No se habrán iniciado subprocesos adicionales, no está cambiando el código de salida (que por defecto es 0), básicamente no tiene sentido.

Cuando los documentos dicen que el método nunca regresa normalmente, significa que la línea de código posterior es efectivamente inalcanzable, aunque el compilador no sabe que:

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

Se lanzará una excepción o la VM terminará antes de regresar. Nunca "simplemente regresará".

Es muy raro que valga la pena llamar a System.exit()IME. Puede tener sentido si está escribiendo una herramienta de línea de comandos y desea indicar un error a través del código de salida en lugar de simplemente lanzar una excepción ... pero no recuerdo la última vez que lo usé en el código de producción normal .

Jon Skeet
fuente
2
¿Por qué el compilador no lo sabe? ¿No es System.exit () lo suficientemente especial como para garantizar un código de detección específico?
Bart van Heukelom
3
@Bart: No, no lo creo. Poner casos especiales en el idioma para cosas como esta aumenta la complejidad del lenguaje con muy pocos beneficios.
Jon Skeet
Pensé que "nunca regresa normalmente" tenía que ver con la "finalización abrupta" de la declaración. (JLS sección 14.1). ¿Me equivoco?
aioobe
@aioobe: No, tienes razón. System.exit()nunca se completará normalmente: siempre se completará con una excepción o con el cierre de la máquina virtual (que en realidad no está cubierto en el JLS).
Jon Skeet
1
@piechuckerr: ¿Qué te hace pensar eso? Es completamente razonable llamarlo con un valor distinto de 0, para indicarle al shell que realiza la llamada (o lo que sea) que el programa ha encontrado un error.
Jon Skeet
15

El método nunca regresa porque es el fin del mundo y ninguno de sus códigos se ejecutará a continuación.

Su aplicación, en su ejemplo, saldría de todos modos en el mismo lugar en el código, pero si usa System.exit. tiene la opción de devolver un código personalizado al entorno, por ejemplo, digamos

System.exit(42);

¿Quién va a hacer uso de su código de salida? Un script que llamó a la aplicación. Funciona en Windows, Unix y todos los demás entornos programables.

¿Por qué devolver un código? Para decir cosas como "No tuve éxito", "La base de datos no respondió".

Para ver cómo obtener el valor de un código de salida y usarlo en un script de shell de Unix o un script de cmd de Windows, puede consultar esta respuesta en este sitio

mico
fuente
14

System.exit(0)termina la JVM. En ejemplos simples como este es difícil percibir la diferencia. El parámetro se devuelve al sistema operativo y normalmente se usa para indicar una terminación anormal (por ejemplo, algún tipo de error fatal), por lo que si llama a Java desde un archivo por lotes o script de shell, podrá obtener este valor y tener una idea si la solicitud fue exitosa

Tendría un gran impacto si llamaras System.exit(0)a una aplicación implementada en un servidor de aplicaciones (piénsalo antes de probarlo).

Qwerky
fuente
12

En aplicaciones que pueden tener complejas conexiones de cierre, este método no debe llamarse desde un hilo desconocido. System.exitnunca sale normalmente porque la llamada se bloqueará hasta que se termine la JVM. Es como si el código que se está ejecutando tiene el enchufe de alimentación conectado antes de que pueda terminar. Las llamadas System.exitiniciarán los ganchos de cierre del programa y cualquier subproceso que System.exitbloquee hasta que finalice el programa. Esto tiene la implicación de que si el gancho de apagado a su vez envía una tarea al subproceso desde el que System.exitse llamó, el programa se bloqueará.

Estoy manejando esto en mi código con lo siguiente:

public static void exit(final int status) {
    new Thread("App-exit") {
        @Override
        public void run() {
            System.exit(status);
        }
    }.start();
}
djechlin
fuente
También tuve el problema de que System.exit en realidad no terminaría la JVM. Sin embargo, solo sucedió bajo ciertas condiciones. Un threadump que tomé no me dio ninguna idea sobre qué hilos / controladores de apagado están bloqueando el apagado. ¡Usar el código descrito en el comentario me ayudó a resolverlo!
Kai
7

Aunque la respuesta fue realmente útil, pero de alguna manera se perdió algunos detalles adicionales. Espero que a continuación le ayude a comprender el proceso de apagado en Java, además de la respuesta anterior:

  1. En un apagado * ordenado, la JVM primero inicia todos los ganchos de apagado registrados. Los ganchos de cierre son subprocesos no iniciados que están registrados con Runtime.addShutdownHook.
  2. JVM no garantiza el orden en que se inician los ganchos de apagado. Si algún subproceso de aplicación (daemon o no daemon) todavía se está ejecutando en el momento del apagado, continuará ejecutándose simultáneamente con el proceso de apagado .
  3. Cuando todos los ganchos de cierre se hayan completado, la JVM puede elegir ejecutar finalizadores si runFinalizersOnExit es verdadero y luego se detiene.
  4. JVM no intenta detener o interrumpir ningún subproceso de aplicación que todavía se esté ejecutando en el momento del apagado; se terminan abruptamente cuando la JVM finalmente se detiene.
  5. Si los ganchos de cierre o finalizadores no se completan, entonces el proceso de apagado ordenado se "cuelga" y la JVM debe cerrarse abruptamente.
  6. En un cierre abrupto, la JVM no está obligada a hacer otra cosa que detener la JVM; los ganchos de apagado no se ejecutarán.

PD: La JVM puede apagarse en un ordenada o abrupta .

  1. Se inicia un apagado ordenado cuando finaliza el último subproceso "normal" (no demonio), alguien llama a System.exit o por otros medios específicos de la plataforma (como enviar un SIGINT o presionar Ctrl-C).
  2. Si bien arriba es la forma estándar y preferida para que la JVM se apague, también se puede cerrar abruptamente llamando a Runtime.halt o eliminando el proceso JVM a través del sistema operativo (como enviar un SIGKILL).
Juan
fuente
7

NUNCA se debe llamar System.exit(0)por estos motivos:

  1. Es un "goto" y "gotos" ocultos que rompen el flujo de control. Confiar en ganchos en este contexto es un mapeo mental que todos los desarrolladores del equipo deben tener en cuenta.
  2. Salir del programa "normalmente" proporciona el mismo código de salida al sistema operativo, por System.exit(0)lo que es redundante.

    Si su programa no puede salir "normalmente", ha perdido el control de su desarrollo [diseño]. Siempre debe tener el control total del estado del sistema.

  3. Los problemas de programación como la ejecución de subprocesos que no se detienen normalmente se ocultan.
  4. Puede encontrar un estado de aplicación inconsistente que interrumpe los subprocesos de manera anormal. (Consulte el n. ° 3)

Por cierto: devolver otros códigos de retorno distintos de 0 tiene sentido si desea indicar la finalización anormal del programa.

oopexpert
fuente
2
"Siempre debe tener el control total del estado del sistema". Eso simplemente no es posible en Java. Los frameworks de IoC y los servidores de aplicaciones son solo 2 formas muy populares y ampliamente utilizadas de renunciar a su control del estado del sistema. Y eso está totalmente bien.
mhlz
Ejecute System.exit (0) en su servidor de aplicaciones y cuénteme sobre las reacciones del administrador de su servidor ... Se perdió el tema de esta pregunta y mi respuesta.
oopexpert
Tal vez no fui lo suficientemente claro. Siempre debe tener el control total del estado del sistema del que es responsable. Sí, algunas responsabilidades se trasladaron a servidores de aplicaciones e IoC. Pero no estaba hablando de eso.
oopexpert
5

Se necesita System.exit

  • cuando desea devolver un código de error que no sea 0
  • cuando quieras salir de tu programa desde un lugar que no sea main ()

En su caso, hace exactamente lo mismo que el simple return-from-main.

mfx
fuente
¿Qué quieres decir con esto último? La forma correcta de cerrar el programa es detener todos los hilos (muy bien), un movimiento que no tiene que iniciarse desde el hilo principal, por lo que no exites su única opción allí.
Bart van Heukelom
@Bart: lo que describe es una de las formas posibles de cerrar un programa, no la forma correcta . No hay nada de malo en usar ganchos de apagado para detener todos los hilos de manera agradable. Y se lanzan ganchos de apagado con exit. No es la única opción, pero definitivamente es una opción que vale la pena considerar.
Joonas Pulakka
Pero lo que deduzco de los documentos, los ganchos de cierre son muy delicados y puede que no tengan mucho tiempo para hacer lo que quieres que hagan. Por supuesto, úselos para salir bien en caso de un apagado del sistema operativo o algo así, pero si va a llamar a System.exit () usted mismo, creo que es mejor hacer el código de apagado antes (después de lo cual probablemente no necesita System.exit () más)
Bart van Heukelom
1
Los ganchos de cierre tienen todo el tiempo que tardan en terminar. La VM no se detiene antes de que todos los ganchos hayan regresado. De hecho, es posible evitar que la máquina virtual se cierre registrando un enlace de apagado que tarda mucho tiempo en ejecutarse. Pero, por supuesto, es posible hacer la secuencia de apagado de varias maneras alternativas.
Joonas Pulakka
Volver de main no se cerrará si hay otros hilos que no sean daemon.
djechlin
4

La especificación del lenguaje Java dice que

Salida del programa

Un programa finaliza toda su actividad y se cierra cuando ocurre una de dos cosas:

Todos los hilos que no son demonios terminan.

Algunos subprocesos invocan el método de salida de class Runtime o class System , y el administrador de seguridad no prohíbe la operación de salida.

Significa que debe usarlo cuando tiene un programa grande (bueno, al menos más grande que este) y desea finalizar su ejecución.

Marcin Szymczak
fuente
3

Si tiene otro programa ejecutándose en la JVM y usa System.exit, ese segundo programa también se cerrará. Imagine, por ejemplo, que ejecuta un trabajo java en un nodo del clúster y que el programa java que administra el nodo del clúster se ejecuta en la misma JVM. Si el trabajo usara System.exit, no solo saldría del trabajo sino que también "cerraría el nodo completo". No podrá enviar otro trabajo a ese nodo del clúster ya que el programa de administración se ha cerrado accidentalmente.

Por lo tanto, no use System.exit si desea poder controlar su programa desde otro programa java dentro de la misma JVM.

Use System.exit si desea cerrar la JVM completa a propósito y si desea aprovechar las posibilidades que se han descrito en las otras respuestas (por ejemplo, desconectar ganchos: gancho de apagado de Java , valor de retorno distinto de cero para la línea de comandos llamadas: cómo obtener el estado de salida de un programa Java en un archivo por lotes de Windows ).

También eche un vistazo a las excepciones de tiempo de ejecución: System.exit (num) o arroje una RuntimeException desde main?

Stefan
fuente