2013-07-25 6 views
5

Ecco il mio codicePerché non posso ottenere PhantomReference da ReferenceQueue per un oggetto finalizzabile?

public class FinalizableObject { 

    @Override 
    protected void finalize() throws Throwable { 
     System.out.println("finalize() invoked for " + this); 
     super.finalize(); 
    } 
} 

public class Main { 
    private static void test() throws InterruptedException { 
     ReferenceQueue<FinalizableObject> rq = new ReferenceQueue<FinalizableObject>(); 
     FinalizableObject obj = new FinalizableObject(); 
     PhantomReference<FinalizableObject> pr1 = new PhantomReference<FinalizableObject>(obj, rq); 
     obj = null; 
     System.gc(); 
     Reference<? extends Object> ref = rq.remove(); 
     System.out.print("remove " + ref + " from reference queue\n"); 
    } 
    public static void main(String[] args) { 
     try { 
      test(); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

E 'molto strano, rq.remove() sarà bloccato per sempre. Perché il riferimento fantasma dell'oggetto finalizzabile non può essere inserito nella coda di riferimento? È stato raccolto GC?

risposta

1

Il problema riguarda il metodo non banale finalize(). Nell'implementazione predefinita (nella classe Object) questo metodo è effettivamente vuoto. Quando la sua implementazione non è vuota, non è garantito che l'oggetto verrà immediatamente raccolto dopo aver richiamato finalize().

Se si modificare il programma in tale stile:

for (int i = 0; i < 1000; i++) 
     System.gc(); 

vale a dire che si chiamerà GC più di una volte - si potrebbe portare l'oggetto rq essere pienamente raccolti (test sulla mia macchina).

Inoltre, vi consiglio di seguito i link da esplorare:

  1. Java: PhantomReference, ReferenceQueue and finalize
  2. Discovering Objects with Non-trivial Finalizers
  3. The Secret Life Of The Finalizer: page 2 of 2

UPD: Un altro modo: devi tenere a mente, che i metodi non banali finalize() vengono richiamati da Finalizer-Thread speciale, che anche h essere raccolti Così, per la piena pr raccolta si possono fare queste cose:

a) creare bandiera all'interno Main metodo:

public static volatile boolean flag; 

b) flag impostato in finalize() metodo:

@Override 
protected void finalize() throws Throwable { 
    System.out.println("finalize() invoked for " + this); 
    super.finalize(); 
    Main.flag = true; 
} 

c) controllare la bandiera per vero e quindi chiamare gc() di nuovo:

System.gc(); 
    while (!flag) Thread.sleep(10); 
    System.gc(); 
+0

Grazie mille.
Conosco FinalizerReferenceQueue e FinalizerDeamon, 2 volte GC può raccogliere completamente un oggetto finalizzabile. Ma non riesco a capire il motivo per cui 100 volte il GC attiverà il ritiro (anche 20 volte il GC non può attivare l'accodamento).
D'altra parte, se sostituisco PhantomReference con WeakReference, è possibile rimuovere il riferimento molto rapidamente.

+0

@DevboardFan Questo perché per invocare il metodo 'finalize()' non banale, JVM costruisce Finalizer-Thread speciale, che deve anche essere raccolto e completato. – Andremoniy

+0

Grazie mille! –

Problemi correlati