2013-08-04 7 views
12

Come sarà il filo Finalizer do se c'è un ciclo infinito o deadlock nel Java metodo finalizzare.Come sarà il filo Finalizer fare se c'è un ciclo infinito o stallo del Java finalizzare metodo

+4

Puoi condividere con noi qualsiasi comportamento osservato durante il tentativo? –

+0

È difficile da osservare, JVM non garantisce nemmeno che 'finalize' sarà chiamato, gli aghi per dire che non si sa quando verrà chiamato – morgano

+0

@morgano Un semplice' println' è sufficiente. Ho giocato con questo molto tempo fa, 'finalize' viene chiamato abbastanza rapidamente. Ci sono solo rari casi in cui alcuni oggetti irraggiungibili non vengono finalizzati fino allo spegnimento del sistema. –

risposta

13

Le specifiche scrive:

Prima l'archiviazione per un oggetto venga recuperato dal garbage collector, la Java Virtual Machine invocherà il finalizzatore di quell'oggetto.

Il linguaggio di programmazione Java non specifica quanto tempo verrà richiamato un finalizzatore, tranne che per dire che accadrà prima che la memoria per l'oggetto venga riutilizzata.

Ho letto questo per indicare che il finalizzatore deve essere completato prima che lo spazio di archiviazione possa essere riutilizzato.

Il linguaggio di programmazione Java non specifica quale thread richiamerà il finalizzatore per qualsiasi oggetto specificato.

È importante notare che molti thread di finalizzazione possono essere attivi (a volte è necessario su multiprocessore di memoria condivisa di grandi dimensioni) e che se una grande struttura di dati connessa diventa spazzatura, tutti i metodi di finalizzazione per ogni oggetto in quello la struttura dei dati può essere invocata allo stesso tempo, ogni richiamo del finalizzatore viene eseguito in un thread diverso.

Cioè, finalizzazione possono verificarsi nel thread garbage collector, in un thead separato, o anche una piscina thread separato.

Una JVM non può interrompere semplicemente l'esecuzione di un finalizzatore e può utilizzare solo un numero finito di thread (i thread sono risorse del sistema operativo e i sistemi operativi non supportano arbitrariamente molti thread). Pertanto, i finalizzatori non terminanti dovranno quindi morire di fame quel pool di thread, inibendo così la raccolta di oggetti finalizzabili e causando una perdita di memoria.

Il seguente programma di test conferma questo comportamento:

public class Test { 

    byte[] memoryHog = new byte[1024 * 1024]; 

    @Override 
    protected void finalize() throws Throwable { 
     System.out.println("Finalizing " + this + " in thread " + Thread.currentThread()); 
     for (;;); 
    } 

    public static void main(String[] args) { 
     for (int i = 0; i < 1000; i++) { 
      new Test(); 
     } 
    } 
} 

su Oracle JDK 7, questo stampe:

Finalizing [email protected] in thread Thread[Finalizer,8,system] 
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 
     at tools.Test.<init>(Test.java:5) 
     at tools.Test.main(Test.java:15) 
+1

+1, ha più o meno lo stesso test e ha ottenuto lo stesso risultato. È coinvolto solo un thread finalizzatore. –

+0

@mriton, qual è il significato di ** _ È importante notare che molti thread di finalizzazione possono essere attivi (a volte è necessario su multiprocessore di memoria condivisa di grandi dimensioni) _ **. Ha qualche connessione con l'algoritmo GC che abbiamo scelto, come CMS. E c'è una relazione tra il numero di thread di Finalizer e l'algoritmo GC. – andy

+1

Una JVM può fare ciò che vuole, e diverse JVM possono farlo in modo diverso. Se sei interessato a una particolare JVM, controlla la sua documentazione (o più probabilmente il suo codice sorgente). L'anwer di lpiepiora sembra indicare che esiste sempre un singolo thread di finalizzazione in Oracle JVM, indipendentemente dall'algoritmo di garbage collection in uso. – meriton

4

direi che dal momento che la specifica Java non dice come deve essere invocato il metodo finalize (solo che deve essere invocato, prima che l'oggetto sia garbage collection), il comportamento è specifico dell'implementazione.

La specifica non esclude avere più thread l'esecuzione del processo, ma non lo richiede:

È importante notare che molte discussioni Finalizer possono essere attivi (questo è talvolta necessario su grandi multiprocessori di memoria condivisa) e che se una grande struttura di dati connessa diventa spazzatura, tutti i metodi finalizzati per ogni oggetto in quella struttura dati potrebbero essere invocati allo stesso tempo, , ogni richiamo di finalizzatore in esecuzione in un thread diverso .

Guardando le sorgenti del JDK7, il FinalizerThread mantiene la coda di oggetti in programma per la messa a punto (in realtà gli oggetti vengono aggiunti alla coda da parte del GC, quando dimostrato di essere irraggiungibile - controllare ReferenceQueue doc):

private static class FinalizerThread extends Thread { 
    private volatile boolean running; 
    FinalizerThread(ThreadGroup g) { 
     super(g, "Finalizer"); 
    } 
    public void run() { 
     if (running) 
      return; 
     running = true; 
     for (;;) { 
      try { 
       Finalizer f = (Finalizer)queue.remove(); 
       f.runFinalizer(); 
      } catch (InterruptedException x) { 
       continue; 
      } 
     } 
    } 
} 

Ogni oggetto viene rimosso dalla coda e il metodo runFinalizer viene eseguito su di esso. Il controllo viene eseguito se la finalizzazione è stata eseguita sull'oggetto e, in caso contrario, come chiamata a un metodo nativo invokeFinalizeMethod. Il metodo semplicemente sta chiamando il metodo finalize sull'oggetto:

JNIEXPORT void JNICALL 
Java_java_lang_ref_Finalizer_invokeFinalizeMethod(JNIEnv *env, jclass clazz, 
                jobject ob) 
{ 
    jclass cls; 
    jmethodID mid; 

    cls = (*env)->GetObjectClass(env, ob); 
    if (cls == NULL) return; 
    mid = (*env)->GetMethodID(env, cls, "finalize", "()V"); 
    if (mid == NULL) return; 
    (*env)->CallVoidMethod(env, ob, mid); 
} 

Ciò dovrebbe portare ad una situazione in cui gli oggetti vengono accodati nella lista, mentre il FinalizerThread viene bloccato sull'oggetto difettoso, che a sua volta dovrebbe portare a OutOfMemoryError.

Quindi, per rispondere alla domanda iniziale:

quale sarà il filo Finalizer fare se v'è un ciclo infinito o di stallo in Java metodo Finalize.

Si siederà semplicemente lì ed eseguirà quel ciclo infinito fino a OutOfMemoryError.

public class FinalizeLoop { 
    public static void main(String[] args) { 
     Thread thread = new Thread() { 
      @Override 
      public void run() { 
       for (;;) { 
        new FinalizeLoop(); 
       } 
      } 
     }; 
     thread.setDaemon(true); 
     thread.start(); 
     while (true); 
    } 

    @Override 
    protected void finalize() throws Throwable { 
     super.finalize(); 
     System.out.println("Finalize called"); 
     while (true); 

    } 
} 

Nota "Finalize called" se stampato una sola volta su JDK6 e JDK7.

+0

scusa, non sono d'accordo con il tuo codice di esempio qui sopra. il ciclo for terminerà allo stesso tempo del thread principale, quindi ** non puoi ** vedere nulla. – andy

+0

@andy hai ragione, ho cambiato leggermente il codice per assicurarmi che tu possa vedere qualcosa alla fine - immagino che dipenda da quando il tuo GC calcia. Grazie! – lpiepiora

0

Gli oggetti non saranno "liberati", cioè la memoria non verrà reclamata da loro e anche le risorse che vengono liberate nel metodo di finalizzazione rimarranno riservate in tutto.

Fondamentalmente c'è una coda che contiene tutti gli oggetti in attesa del loro metodo finalize() da eseguire. Il thread di Finalizer preleva gli oggetti da questa coda - viene eseguito finalize - e rilascia l'oggetto.

Se questo thread sarà deadlock, la coda ReferenceQueue crescerà e ad un certo punto l'errore OOM diventerà inesorabile. Anche le risorse verranno bloccate dagli oggetti in questa coda. Spero che questo ti aiuti!!

for(;;) 
{ 
    Finalizer f = java.lang.ref.Finalizer.ReferenceQueue.remove(); 
    f.get().finalize(); 
} 
Problemi correlati