2010-04-10 19 views
16

Tengo un programa Java que se inicia a través de ProcessBuilder desde otro programa Java. System.exit(0) se llama desde el programa secundario, pero para algunos de nuestros usuarios (en Windows) el proceso java.exe asociado con el hijo no finaliza. El programa secundario no tiene ganchos de apagado, ni tiene un SecurityManager que podría detener System.exit() de la terminación de la máquina virtual. No puedo reproducir el problema yo mismo en Linux o Windows Vista. Hasta ahora, los únicos informes del problema provienen de dos usuarios de Windows XP y un usuario de Vista, usando dos JRE diferentes (1.6.0_15 y 1.6.0_18), pero pueden reproducir el problema todo el tiempo.¿Qué puede hacer que Java siga funcionando después de System.exit()?

¿Alguien puede sugerir razones por las cuales la JVM no podría terminar después de System.exit(), y luego solo en algunas máquinas?

Edit 1: Solicité al usuario que instalara el JDK para que pudiéramos obtener un volcado de subprocesos de la máquina virtual infractora. Lo que el usuario me dijo es que el proceso VM desaparece de VisualVM tan pronto como hace clic en el elemento 'Salir' de mi menú --- pero, de acuerdo con el Administrador de tareas de Windows, el proceso no ha terminado, y no importa cuánto tiempo el usuario espera (minutos, horas), nunca termina.

Editar 2: He confirmado ahora que Process.waitFor() en el programa principal nunca regresa para al menos uno de los usuarios que tienen el problema. Entonces, para resumir: la máquina virtual hija parece estar muerta (VisualVM ni siquiera la ve) pero el padre aún ve el proceso como vivo y también lo hace Windows.

Respuesta

5

El proceso padre tiene un hilo dedicado al consumo de cada uno de stdout y stderr del niño (que pasa que emiten a través de un archivo de registro ). Por lo que puedo ver, los que están funcionando correctamente, ya que estamos viendo toda la salida que esperamos ver en el registro de

tuve un problema similar con mi programa no desaparecer de mgr tarea cuando Estaba consumiendo stdout/stderr. en mi caso, si cerré la transmisión que estaba escuchando antes de llamar a system.exit(), entonces el javaw.exe se colgó. extraña, que no estaba escribiendo a la corriente ...

la solución en mi caso era simplemente eliminar la corriente en lugar de cerrarla antes de existir. por supuesto, siempre puedes enjuagar y luego volver a redirigir a stdout y stderr antes de salir.

1

¿Quizás un finalizador mal escrito? Mi primer pensamiento fue abrir un gancho de cierre cuando leí el asunto. Especulación: ¿un hilo que capta InterruptedException y sigue funcionando de todos modos mantiene el proceso de salida?

Me parece que si el problema es reproducible, debería ser capaz de conectarse a la JVM y obtener una lista de hilos/seguimiento de pila que muestre lo que está colgado.

¿Estás seguro de que el niño todavía está funcionando realmente y que no se trata solo de un proceso zombie no convertido?

+0

Si se trata de un finalizador, entonces no es una en nuestro código --- que don' t tiene alguno.Estoy seguro de que el hijo aún se está ejecutando, ya que vemos dos procesos de Java y el usuario no tiene ningún otro programa en su sistema que use Java. Esperaba evitar pedirle que instalara el JDK para poder usar jstack, pero creo que esa podría ser la mejor manera de proceder. – uckelman

+0

Lo que quise decir sobre el proceso secundario es, ¿le quedan hilos? ¿Todavía está respondiendo a la entrada? ¿Todavía tiene asas abiertas? Estoy más familiarizado con Unix que con Windows, pero sé que los procesos no se borran del sistema operativo Unix hasta que el proceso principal los recoge y creo que es lo mismo en Windows. El hecho de que aparezca en la parte superior o en el Administrador de Tareas no significa que todavía esté activo. Por supuesto, eso no explica por qué solo le sucede a algunos usuarios ... –

+1

De manera predeterminada, los finalizadores no se ejecutan al salir; ver http://java.sun.com/javase/6/docs/api/java/lang/System.html#runFinalizersOnExit(boolean) –

1

¿El proceso principal consume el error- y outputstream del proceso hijo? Si bajo algunos sistemas operativos el proceso hijo imprime algunos errores/advertencias en stdout/stderr y el proceso principal no está consumiendo las transmisiones, el proceso hijo se bloqueará y no llegará a System.exit();

+0

El proceso principal tiene un hilo dedicado a consumir cada uno de STDOUT y STDERR del niño (que pasa ese resultado a un archivo de registro). Hasta donde puedo ver, están funcionando correctamente, ya que estamos viendo todo el resultado que esperamos ver en el registro. – uckelman

3

Aquí hay un par de escenarios ...

por la definición de un hilo en http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Thread.html

...

Cuando una máquina virtual Java se pone en marcha, por lo general hay un solo país que no -daemon thread (que típicamente llama al método llamado main de alguna clase designada). Java Virtual Machine continúa ejecutando subprocesos hasta que ocurra cualquiera de los siguientes:

1) Se ha llamado al método de salida de la clase Runtime y el administrador de seguridad ha permitido que la operación de salida tenga lugar. 2) Todos los hilos que no son hilos daemon han muerto, ya sea al regresar de la llamada al método de ejecución o al lanzar una excepción que se propaga más allá del método de ejecución.

Otra posibilidad es si se ha llamado al método runFinalizersOnExit. según la documentación en http://java.sun.com/j2se/1.4.2/docs/api/java/lang/System.html Obsoleto.Este método es inherentemente inseguro. Puede hacer que se llamen finalizadores a objetos en vivo mientras que otros hilos manipulan esos objetos al mismo tiempo, lo que da como resultado un comportamiento errático o un interbloqueo. Habilitar o deshabilitar la finalización al salir; al hacerlo, especifica que los finalizadores de todos los objetos que tienen finalizadores que aún no se han invocado automáticamente deben ejecutarse antes de que finalice el tiempo de ejecución de Java. Por defecto, la finalización al salir está deshabilitada. Si hay un administrador de seguridad, su método checkExit se llama primero con 0 como argumento para garantizar que se permita la salida. Esto podría generar una SecurityException.

+0

No tenemos llamadas a 'runFinalizersOnExit', ni tenemos' SecurityManager', así que creo que estas no son la causa. – uckelman

0

Creo que todos han sido cubiertas provisionalmente las causas obvias; p.ej. finalizadores, ganchos de cierre, no drenando correctamente la salida estándar/error estándar en el proceso principal. Ahora necesita más evidencia para entender lo que está pasando.

Sugerencias:

  1. configurar una máquina de Windows XP o Vista (o virtual), instale el JRE correspondiente y su aplicación, y trate de reproducir el problema. Una vez que pueda reproducir el problema, adjunte un depurador o envíe la señal correspondiente para obtener un volcado de hilo a error estándar.

  2. Si no puede reproducir el problema que el anterior, conseguir uno de sus usuarios a tomar un vertedero de hilo, y reenviar el archivo de registro.

+0

No puedo reproducirlo en Vista, y el usuario no puede obtener un volcado de subprocesos de VisualVM, ya que la VM parece que ya no está activa. – uckelman

0

Otro caso no mencionado aquí es si los ganchos de apagado cuelgan de algo, así que tenga cuidado al escribir el código de cierre de cierre (y si una biblioteca de terceros registró un gancho de apagado también colgado).

Dean

1

Hy tuve el mismo problema, pero el Couse para mí era yo estaba usando la depuración remota (VmArgs: -Xdebug -Xrunjdwp: transport = dt_socket, dirección =% puerto%, servidor = y, suspend = y) cuando deshabilité esto, el proceso java.exe salió como se esperaba.

0

Esto puede suceder si el código (o una biblioteca que usa) tiene un gancho de cierre o un finalizador que no termina limpiamente.

A más vigorosa (por lo que sólo debe utilizarse en casos extremos!) Manera de forzar el apagado es ejecutando:

Runtime.getRuntime().halt(0); 
Cuestiones relacionadas