2010-04-10 3 views
16

Ho un programma Java che viene avviato tramite ProcessBuilder da un altro programma Java. System.exit(0) viene chiamato dal programma figlio, ma per alcuni dei nostri utenti (su Windows) il processo java.exe associato al figlio non viene terminato. Il programma figlio non ha hook di chiusura, né ha un SecurityManager che potrebbe interrompere System.exit() dal termine della VM. Non riesco a riprodurre il problema da solo su Linux o Windows Vista. Finora, le uniche segnalazioni del problema provengono da due utenti di Windows XP e un utente di Vista, utilizzando due JRE diversi (1.6.0_15 e 1.6.0_18), ma sono in grado di riprodurre il problema ogni volta.Che cosa può far continuare a funzionare Java dopo System.exit()?

Qualcuno può suggerire motivi per cui la JVM non sarebbe riuscita a terminare dopo System.exit() e quindi solo su alcune macchine?

Modifica 1: Ho ottenuto l'utente di installare il JDK in modo da poter ottenere un dump di thread dalla VM offensiva. Quello che l'utente mi ha detto è che il processo VM scompare da VisualVM non appena fa clic sull'elemento 'Esci' nel mio menu --- ma, secondo Task Manager di Windows, il processo non è terminato, e non importa quanto a lungo l'utente attende (minuti, ore), non termina mai.

Edit 2: mi hanno confermato ora che Process.waitFor() nel programma principale non torna per almeno uno degli utenti che hanno il problema. Quindi, per riassumere: Il bambino VM sembra essere morto (VisualVM non lo vede nemmeno), ma il genitore vede ancora il processo come vivo e così fa Windows.

risposta

5

Il processo genitore ha un thread dedicata al consumo di ciascuna delle STDOUT del bambino e STDERR (che passa che la produzione attraverso un file di log ). Per quanto posso vedere, quelli sono funziona correttamente, dal momento che stiamo vedendo tutto l'output ci aspettiamo di vedere nel registro

ho avuto un problema simile con il mio programma non scomparire dal compito mons quando stavo consumando lo stdout/stderr. nel mio caso, se ho chiuso il flusso che stava ascoltando prima di chiamare system.exit(), javaw.exe si è bloccato. strano, non stava scrivendo allo stream ...

la soluzione nel mio caso era semplicemente scaricare lo stream piuttosto che chiuderlo prima di esistere. ovviamente, puoi sempre svuotare e poi reindirizzare nuovamente a stdout e stderr prima dell'uscita.

1

Forse un finalizzatore scritto male? Un gancio di arresto è stato il mio primo pensiero quando ho letto la riga dell'oggetto. Speculazione: un thread che intercetta InterruptedException e continua a funzionare in ogni caso, mantiene il processo di uscita?

Mi sembra che se il problema è riproducibile, dovresti essere in grado di collegarti alla JVM e ottenere una lista di thread/traccia di stack che mostri ciò che è bloccato.

Sei sicuro che il bambino è ancora in esecuzione e che non si tratta solo di un processo di zombi non ancora eseguito?

+0

Se si tratta di un finalizzatore, allora non è uno nel nostro codice --- noi don' ne hoSono sicuro che il bambino è ancora in esecuzione, dal momento che vediamo due processi Java e l'utente non ha altri programmi sul suo sistema che usano Java. Speravo di evitare di chiedergli di installare il JDK in modo da poter usare jstack, ma ora penso che potrebbe essere il modo migliore di procedere. – uckelman

+0

Ciò che intendevo dire del processo figlio è che sono rimasti dei fili? Sta ancora rispondendo all'input? Ha ancora le maniglie aperte? Ho più familiarità con Unix di Windows, ma so che i processi non vengono cancellati dal sistema operativo Unix fino a quando il processo padre non li raccoglie e penso che sia lo stesso su Windows. Solo perché appare in alto o TaskManager non significa che sia ancora attivo. Ovviamente, questo non spiega perché succede solo ad alcuni utenti ... –

+1

Per impostazione predefinita i finalizzatori non vengono eseguiti all'uscita; vedi http://java.sun.com/javase/6/docs/api/java/lang/System.html#runFinalizersOnExit(boolean) –

1

Il processo padre utilizza l'errore e l'output del processo figlio? Se in alcuni sistemi operativi il processo figlio stampa alcuni errori/avvertimenti su stdout/stderr e il processo padre non sta consumando i flussi, il processo figlio bloccherà e non raggiungerà System.exit();

+0

Il processo padre ha un thread dedicato al consumo di ogni STDOUT e STDERR del bambino (che trasmette tale output a un file di registro). Per quanto posso vedere, quelli stanno funzionando correttamente, visto che stiamo vedendo tutti gli output che ci aspettiamo di vedere nel log. – uckelman

3

Qui ci sono un paio di scenari ...

Per la definizione di un thread in http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Thread.html

...

Quando una Java Virtual Machine avvia, di solito c'è una sola non -daemon thread (che in genere chiama il metodo denominato main di alcune classi designate). Java Virtual Machine continua ad eseguire i thread fino a quando si verifica uno dei seguenti casi:

1) È stato chiamato il metodo di uscita di class Runtime e il gestore sicurezza ha consentito l'operazione di uscita. 2) Tutti i thread che non sono thread daemon sono morti, restituendo dalla chiamata al metodo run o generando un'eccezione che si propaga oltre il metodo run.

Un'altra possibilità è se è stato chiamato il metodo runFinalizersOnExit. come da documentazione in http://java.sun.com/j2se/1.4.2/docs/api/java/lang/System.html Obsoleto.Questo metodo è intrinsecamente pericoloso. È possibile che i finalizzatori vengano richiamati su oggetti attivi mentre altri thread stanno simultaneamente manipolando tali oggetti, causando comportamenti irregolari o deadlock. Abilita o disabilita la finalizzazione all'uscita; così facendo si specifica che i finalizzatori di tutti gli oggetti che hanno finalizzatori che non sono stati ancora richiamati automaticamente devono essere eseguiti prima che il runtime Java venga chiuso. Per impostazione predefinita, la finalizzazione all'uscita è disabilitata. Se esiste un gestore sicurezza, il suo metodo checkExit viene prima chiamato con 0 come argomento per assicurarsi che l'uscita sia consentita. Ciò potrebbe comportare un SecurityException.

+0

Non abbiamo chiamate a 'runFinalizersOnExit', né abbiamo un' SecurityManager', quindi penso che non siano queste le cause. – uckelman

0

Penso che tutte le cause ovvie siano state coperte provvisoriamente; per esempio. finalizzatori, hook di shutdown, non drenando correttamente l'output standard/errore standard nel processo genitore. Ora hai bisogno di più prove per capire cosa sta succedendo.

Suggerimenti:

  1. impostare un Windows XP o macchina Vista (o virtuale), installare il relativo JRE e la vostra applicazione, e provare a riprodurre il problema. Una volta che è possibile riprodurre il problema, allegare un debugger o inviare il segnale pertinente per ottenere un dump del thread su errore standard.

  2. Se non riesci a riprodurre il problema come sopra, chiedi a uno dei tuoi utenti di fare un dump del thread e di inoltrare il file di registro.

+0

Non riesco a riprodurlo da solo su Vista e l'utente non può ottenere un dump di thread da VisualVM, poiché la VM sembra non essere più attiva. – uckelman

0

Un altro caso non menzionato qui è se i ganci di arresto si bloccano su qualcosa, quindi fai attenzione quando scrivi il codice di blocco dell'arresto (e se una libreria di terze parti registra un gancio di arresto anch'esso appeso).

Dean

1

Hy Ho avuto lo stesso problema, ma il couse per me era stavo usando il debug remoto (VmArgs: -Xdebug -Xrunjdwp: trasporti = dt_socket, indirizzo = porta%%, server = y, suspend = y) quando ho disabilitato questo il processo java.exe è uscito come previsto.

0

Ciò può accadere se il codice (o una libreria che si utilizza) ha un hook di chiusura o un finalizzatore che non termina in modo pulito.

A più vigorosa (così dovrebbe essere utilizzato solo in casi estremi!) Modo per forzare l'arresto è quello di eseguire:

Runtime.getRuntime().halt(0);