2011-11-30 9 views
8

Ho una lezione che si tiene a un delegato, per valutare pigramente qualcosa in seguito.GC di delegati, cosa mi manca? (il mio delegato non viene ritirato)

Dopo averlo valutato, chiamando il delegato, ho cancellato il riferimento al delegato, sperando che fosse idoneo per la raccolta. Dopo tutto, potrebbe trattenere un mondo di variabili locali se è stato costruito come metodo anonimo.

Ho provato a creare un test unitario per verificarlo, ma non sembra funzionare come pianificato, invece sembra che sia le mie ipotesi su WeakReference (che ho usato per scopi di test qui), o alcune altro presupposto, non regge l'acqua.

Date un'occhiata a questo codice, che è possibile eseguire in LINQPad

void Main() 
{ 
    WeakReference wr; 
    Lazy<int> l; 
    CreateTestData(out wr, out l); 

    wr.IsAlive.Dump();     // should be alive here 

    GC.Collect(); 
    GC.WaitForPendingFinalizers(); 
    GC.Collect(); 

    wr.IsAlive.Dump();     // and alive here as well 
    l.Value.Dump();      // but now we clear the reference 

    GC.Collect();      // so one of these should collect it 
    GC.WaitForPendingFinalizers(); 
    GC.Collect(); 

    wr.IsAlive.Dump();     // and then it should be gone here 
    GC.KeepAlive(l); 
} 

void CreateTestData(out WeakReference wr, out Lazy<int> l) 
{ 
    Func<int> f =() => 10; 
    wr = new WeakReference(f); 
    l = new Lazy<int>(f); 
} 

public class Lazy<T> 
{ 
    private Func<T> _GetValue; 
    private T _Value; 

    public Lazy(Func<T> getValue) 
    { 
     _GetValue = getValue; 
    } 

    public T Value 
    { 
     get 
     { 
      if (_GetValue != null) 
      { 
       _Value = _GetValue(); 
       _GetValue = null; 
      } 
      return _Value; 
     } 
    } 
} 

mi è stato presupposto che:

  1. Indipendentemente build di debug, debugger, da quando ho creato il delegato in un metodo separato, dal quale ritorno, non ci dovrebbe essere nulla che regge al delegato, ad eccezione degli oggetti WeakReference e Lazy<T>
  2. Se chiedo il Lazy<T> oggetto abbandoni il proprio riferimento al delegato, che ridurrebbe i riferimenti solo quella che WeakReference è partecipazione
  3. E poi forzare una garbage collection completa, supponendo che se l'unico sinistro riferimento è quello in WeakReference
  4. Poi il delegato sarebbero raccolti, e la mia WeakReference indicherebbero che l'oggetto non è più vivo

l'uscita del codice è stato in tal modo dovrebbe essere (con i commenti):

true // not gc'ed after construction 
true // not gc'ed after full GC, still beind held by Lazy<T> 
10 // value from calling delegate 
false // but is now gc'ed, Lazy<T> no longer has a reference to it 

Ma invece l'output è:

true 
true 
10 
true 

Qualcuno può far luce su ciò che mi manca qui?

risposta

6

Il "problema" è che il compilatore ha notato che può riutilizzare una singola istanza delegata per sempre. Non cattura alcun contesto, nemmeno il riferimento implicito this. Quindi questo:

void CreateTestData(out WeakReference wr, out Lazy<int> l) 
{ 
    Func<int> f =() => 10; 
    ... 
} 

è trasformato in qualcosa di simile:

static Func<int> hiddenDelegate; 

static int HiddenMethod() 
{ 
    return 10; 
} 

void CreateTestData(out WeakReference wr, out Lazy<int> l) 
{ 
    if (hiddenDelegate == null) 
    { 
     hiddenDelegate = HiddenMethod; 
    } 

    Func<int> f = hiddenDelegate; 
    ... 
} 

guardare il codice a ildasm (o riflettore senza ottimizzazione abilitata) per vedere esattamente cosa sta succedendo.

+0

Ok, questo ha senso. Ho cambiato il delegato in modo che usasse una variabile locale per calcolarne i risultati, e questo è stato effettivamente raccolto. –

+0

Domanda laterale: presumo che il compilatore non sia abbastanza intelligente da notare che più delegati, dello stesso tipo, che restituiscono lo stesso valore costante, possono essere creati una volta e riutilizzati ovunque? –

+0

Fondamentalmente, il mio obiettivo era di fare un test che verificasse che il riferimento al delegato venisse abbandonato al momento giusto, per rendere possibile la garbage collection dove consentito, non necessariamente quel particolare delegato è stato raccolto.La modifica del delegato ha appena risolto il problema e i test ora vengono eseguiti come previsto. –

Problemi correlati