2009-04-20 13 views
19

Domanda veloce sulla teoria di GCing. Ho il seguente metodo. Funziona ed esce dal metodo. Come mai anche dopo la pubblicazione di GC, il timer esiste ancora e mantiene "TICK" ing? Non credo che ci sia ancora un riferimento al timer o alla timertask dopo che questo metodo esiste, quindi mi aspetto che il timer sia GCed e causi un'eccezione. Per favore aiutami a capire questo concetto.Java: perché non viene raccolta la garbage collection?

Grazie, JBU

private void startTimer() 
    { 
     Timer timer= new Timer(); 
     TimerTask timerTask= new TimerTask() 
     { 

      @Override 
      public void run() 
      { 
       System.out.println("TICK"); 
      } 
     }; 

     timer.scheduleAtFixedRate(timerTask, 
       0, 
       500); 
    } 
+0

Non sarebbe orribile se lo ha fatto andare via? Sarebbe più difficile quasi tutto il multi-tasking. –

risposta

0

Come fai a sapere il Ran GC? La raccolta dei rifiuti in generale non è una cosa deterministica e non è sicuramente innescata dall'ambito del metodo. Non è come il C++ in cui si lascia l'ambito di una funzione e il fuoco dei distruttori. Andrà in giro a raccogliere quella memoria se e quando il GC lo sentirà.

+1

Ho chiamato il GC esplicitamente attraverso la modalità di debug di netbeans. – jbu

33

L'oggetto Timer pianifica effettivamente le attività da eseguire in un thread in background, in modo che il thread in background mantenga un riferimento al Timer (e al TimerTask), che impedisce che vengano raccolte insieme.

Ecco la citazione appropriata dalla documentazione:

Dopo l'ultimo riferimento dal vivo a un oggetto Timer se ne va e tutti compiti in sospeso hanno completato esecuzione, di esecuzione del task filo del timer termina con grazia (e diventa soggetto alla raccolta di immondizia ). Tuttavia, questo può richiedere arbitrariamente a lungo per verificarsi. Per impostazione predefinita, il thread di esecuzione dell'attività non viene eseguito come thread daemon, , quindi è possibile che mantenga una domanda da terminante. Se un chiamante desidera che interrompa rapidamente l'esecuzione del task del timer, il chiamante deve chiamare invocando il metodo di annullamento del timer.

Quindi la condizione che "tutte le attività in sospeso hanno completato l'esecuzione" non è soddisfatta e il thread non termina mai, quindi Timer/TimerTask non è mai stato GC.

+0

Esattamente. Da JavaDocs di Timer: "Corrispondente a ciascun oggetto Timer è un singolo thread in background che viene utilizzato per eseguire tutte le attività del timer, in sequenza." –

+0

Tuttavia, se annullo il timer, il Timer/TimerTask sarà eventualmente sottoposto a GC? – ptikobj

2

Il timer non viene raccolto automaticamente perché è ancora in esecuzione; qualche altro oggetto (ad esempio lo scheduler dei thread) ha ancora un riferimento ad esso, che probabilmente è stato creato all'interno di scheduleAtFixedRate().

12

Poiché un timer ha un background thread that continues running:

corrispondente a ciascun oggetto Timer è un singolo filo di fondo che è utilizzato per eseguire tutte compiti del timer, sequenziale. Le attività del timer dovrebbero essere completate rapidamente. Se l'attività di un contatore richiede troppo tempo per essere completata, "manda" il thread di esecuzione del task del timer . Ciò può, a sua volta, ritardare l'esecuzione delle attività successive , che può "raggrupparsi" ed eseguirsi in rapida successione quando (e se) l'attività incriminata finisce definitivamente.

Poiché si tratta di un thread in background, continua fino a quando la JVM non si chiude o viene interrotta.

Aggiornamento: un po 'di più su questo. Un "thread in background" è la stessa cosa di un thread daemon, chiamato per analogia con un processo daemon BSD. Se vedete i javadocs su Thread, troverete:

Marks questa discussione sia come un thread demone o un thread utente. La macchina virtuale Java termina quando i soli thread in esecuzione sono tutti daemon thread.

Quando termina, tutti i thread utente si interrompono, lasciando solo i thread daemon. Quindi la JVM si spegne. Per un buon tempo, se breve, chiama il numero Thread.currentThread().setDaemon(true); dal principale.

Aggiornamento: Ack. Ho avuto quello quasi a destra. Devi rendere il timer un demone in fase di costruzione. (Forse questo cambiamento, o ho appena avuto un fallimento cervello?)

Comunque, ecco codice di esempio:

import java.util.*; 

class Chatter extends TimerTask { 
    public void run(){ 
     System.err.println("Timer run."); 
    } 
} 

public class TryThread { 
    public static void main(String[] argv){ 
     // If argument is true, only runs a few times. 
     Timer t = new Timer(false); 
     t.schedule(new Chatter(), 1L, 1L); 
     return ; 
    } 
} 
+2

Giusto per sottolineare il punto di Charlie, i Thread in tempo reale sono oggetti "di radice" per il garbage collector. Non sono spazzatura finché non muoiono e non possono essere raccolti. Nulla di fortemente referenziabile da un thread live non è spazzatura. Le classi caricate funzionano in modo simile. – erickson

+0

Attendi, quindi questo timer può continuare ad essere eseguito anche dopo che il programma che lo ha invocato termina? –

+0

No, perché quando termina la public public public void main che è stata invocata all'avvio, la JVM generale si spegne, il che significa fermare e uccidere i thread in background. In realtà non ottiene GC, perché quando il processo muore, tutta la memoria viene rilasciata. –