2011-11-30 23 views
8

我有一個課程,堅持一個代表,以便稍後懶洋洋地評價一些東西。代表GC,我錯過了什麼? (我的代表不收集)

一旦我通過調用委託來評估它,我就清除了對委託的引用,希望它有資格收集。畢竟,如果它是作爲匿名方法構建的,它可能會保留一個局部變量的世界。

我試着建立一個單元測試來驗證這一點,但它似乎並沒有按照我的計劃進行,反而似乎我的假設是關於WeakReference(這裏我用於測試目的)或者一些其他假設,不成立。

看看這段代碼,你可以在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; 
     } 
    } 
} 

運行我假設:

  1. 不管DEBUG編譯器,調試器的連接,因爲我在創建委託單獨的方法,我從哪裏回來,除了WeakReferenceLazy<T>對象
  2. 如果我要求Lazy<T>對象放棄其參考代表,這將減少到只有一個WeakReference是堅持以
  3. 的引用,然後強制完整垃圾回收,假設如果只參考左邊是一個在WeakReference
  4. 然後委託將被收集起來,我WeakReference將指示對象不再活着

代碼的輸出是這樣預計(含評論):

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 

但不是輸出是:

true 
true 
10 
true 

任何人都可以揭示什麼我失蹤這裏一些輕?

回答

6

「問題」是編譯器注意到它可以永久使用單個委託實例。它不捕獲任何上下文,甚至不包含暗示的this參考。所以這個:

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

變成類似:

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; 
    ... 
} 

看代碼的反彙編(或反思,沒有優化打開),看看到底發生了什麼事情。

+0

好的,這是有道理的。我改變了委託,以便它使用一個局部變量來計算它的結果,而這確實是收集的。 –

+0

副題:我假設編譯器不夠聰明,無法注意到同一類型的多個委託返回相同的常量值,可以創建一次並隨處重複使用? –

+0

基本上,我的目標是進行測試,驗證在正確的時間放棄了對委託的引用,以便在允許的情況下進行垃圾回收,而不一定要收集該特定的代表。改變委託只是稍微解決了這個問題,測試現在按預期運行。 –

相關問題