2014-03-04 29 views
5

下面是一個控制檯應用程序,演示了這個問題:代表不要讓垃圾回收

class Program 
{ 
    static void Main() 
    { 
     InitRefs(); 
     GC.Collect(); 
     GC.WaitForPendingFinalizers(); 
     Console.WriteLine(_refObj.IsAlive); 
     Console.WriteLine(_refAction.IsAlive); 
     Console.WriteLine(_refEvent.IsAlive); 
     Console.ReadKey(); 
    } 

    private static void InitRefs() 
    { 
     _refObj = new WeakReference(new object()); 
     _refAction = new WeakReference((Action) (() => { })); 
     _refEvent = new WeakReference(new EventHandler((sender, eventArgs) => { })); 
    } 

    private static WeakReference _refObj; 
    private static WeakReference _refAction; 
    private static WeakReference _refEvent; 
} 

輸出是「假真真」。

我用SOS.dll嘗試查找被GCed持有代表和這裏是我得到的行動:

!gcroot 02472584 
HandleTable: 
    006613ec (pinned handle) 
    -> 03473390 System.Object[] 
    -> 02472584 System.Action 

有人能解釋這是怎麼回事?

+0

@Baldrick,看起來像是。 –

回答

6

你的代表不捕獲任何東西,所以編譯器基本上緩存它們。你可以在這個簡短的程序中看到這個動作:

using System; 

class Program 
{ 
    static void Main() 
    { 
     Action action1 = GetAction(); 
     Action action2 = GetAction(); 
     Console.WriteLine(ReferenceEquals(action1, action2)); // True 
    } 

    private static Action GetAction() 
    { 
     return() => {}; 
    } 
} 

類中有自動生成的靜態字段,這些靜態字段被延遲填充。基本上這是一個優化,以避免創建許多委託對象,這些對象都引用相同的靜態方法而沒有上下文來區分它們。

是的,這意味着代表們本身不會收集垃圾 - 但它們非常輕便(它們不會阻止任何東西其他垃圾收集,因爲它們沒有捕獲任何變量)。

作爲其中代表無法緩存情況的一個例子(並因此有資格進行垃圾回收),該InitRefs方法更改爲:

private static void InitRefs(int x) 
{ 
    _refObj = new WeakReference(new object()); 
    _refAction = new WeakReference((Action) (() => x.ToString())); 
    _refEvent = new WeakReference(new EventHandler((sender, eventArgs) => 
                 x.ToString())); 
} 

那然後打印假三次,因爲代表捕獲x參數,所以不能被緩存。

+0

這很有趣,但我試圖捕獲成員變量,沒有任何改變:_refAction = 新WeakReference((Action)(()=> {if(_refEvent.IsAlive)throw new Exception();})); –

+0

你可以舉一個例子,委託將被垃圾回收嗎? –

+0

@Kefir:沒有捕獲任何東西 - '_refEvent'是一個靜態變量,所以它沒有任何上下文。嘗試添加一個參數到'InitRefs'並捕獲*代替。 –