2012-12-22 11 views
5

Mi sembra che quando uso la classe WeakReference su un metodo delegato di una classe oggetto, la classe dell'oggetto venga raccolta dal GC ma c'è ancora un'altra copia di essa che risiede nello ?GC non raccoglie quando WeakReference fa riferimento a un delegato?

Trovo difficile spiegare a parole. Darò un esempio. Ho la seguente classe di oggetti chiamati TestObject:

class TestObject 
{ 
    public string message = ""; 

    private delegate string deleg(); 

    public TestObject(string msg) 
    { 
     message = msg; 
    } 

    public Delegate GetMethod() 
    { 
     deleg tmp = this.TestMethod; 
     return tmp; 
    } 

    public string TestMethod() 
    { 
     return message; 
    } 

} 

Ora, nella mia applicazione principale, tento di fare riferimento al metodo TestMethod in TestObject tramite un WeakReference. L'intenzione è che il GC TestObject possa essere raccolto da GC quando tutti i riferimenti rigidi sono andati. Ecco come il mio principale si presenta come:

static void Main(string[] args) 
    { 
     var list = new List<WeakReference>(); 
     var obj = new TestObject("Hello 1"); 
     list.Add(new WeakReference(obj.GetMethod())); 
     Console.WriteLine("Initial obj: " + ((Delegate)list[0].Target).DynamicInvoke());  //Works fine 
     obj = null;  //Now, obj is set to null, the TestObject("Hello 1") can be collected by GC 
     GC.Collect(); //Force GC 
     Console.WriteLine("Is obj null: " + ((obj) == null ? "True" : "False")); 
     Console.WriteLine("After GC collection: " + ((Delegate)list[0].Target).DynamicInvoke()); 
     Console.ReadKey(); 
    } 

Questa è l'uscita quando faccio funzionare il codice di cui sopra:

enter image description here

Ecco la cosa strana. Nella prima riga, obj, è possibile stampare "Hello 1" perché è stato appena inizializzato e obj contiene un riferimento allo TestObject. Tutto giusto. Successivamente, obj è stato impostato su null con obj = null e GC è stato costretto a raccogliere. Quindi, nella seconda riga dell'output, obj è true da null. Infine, sull'ultima riga, dal momento che il GC ha ritirato lo obj, mi aspetto che lanci uno NullReferenceException o che non stampi nulla sull'output. Tuttavia, ha effettivamente stampato la stessa cosa della prima riga dell'output! Non dovrebbe già il TestObject essere raccolto dal GC ?!

Questo pone la domanda se il TestObject che è stato trattenuto per la prima volta in obj sia stato successivamente raccolto dal GC o meno dopo aver impostato obj su null.

Se avessi passato l'intero oggetto in WeakReference, ovvero new WeakReference(obj), invece di un delegato nello WeakReference, avrebbe funzionato perfettamente.

Sfortunatamente, nel mio codice, ho bisogno di passare al delegato WeakReference. Come posso fare in modo che WeakReference funzioni correttamente in modo che il GC possa raccogliere l'oggetto semplicemente facendo riferimento a un delegato?

risposta

5

Penso che il problema sia nel test, non nel framework. Sembra che l'impostazione della variabile locale su null non faccia ciò che ti aspetti. Se saltiamo la variabile locale del tutto, si ottiene il NullReferenceException atteso sulla linea 'dopo':

static void Main(string[] args) 
{ 
    var list = new List<WeakReference>(); 
    //var obj = new TestObject("Hello 1"); 
    list.Add(new WeakReference(new TestObject("Hello 1").GetMethod())); 
    Console.WriteLine("Initial obj: " + ((Delegate)list[0].Target).DynamicInvoke());  //Works fine 
    //obj = null;  //Now, obj is set to null, the TestObject("Hello 1") can be collected by GC 
    GC.Collect(); //Force GC 
    //Console.WriteLine("Is obj null: " + ((obj) == null ? "True" : "False")); 
    Console.WriteLine("After GC collection: " + ((Delegate)list[0].Target).DynamicInvoke()); 
    Console.ReadKey(); 
} 
0

In realtà il vostro esempio sarà eseguito come previsto, dal momento che non hai niente, tranne il WeakReference si riferisce al Delegato, così GC può raccoglietelo, anche se commentate la riga "obj = null"!

This is my result

Forse perché sto correndo il codice su un thread separato (a parte il thread principale)?

Problemi correlati