2009-11-11 73 views
0

我似乎無法理解GC.Collect()在存在類重寫Object.Finalize()的行爲。這是我的基本代碼:Object.Finalize()覆蓋和GC.Collect()

namespace test 
{ 
class Foo 
{ 
    ~Foo() { Console.WriteLine("Inside Foo.Finalize()"); } 
} 

static class Program 
{ 

    static void Main() 
    { 
    { 
    Foo bar = new Foo(); 
    } 

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

    Console.ReadLine(); 
    } 
} 

} 

相反的是我期待,我只得到在程序終止控制檯輸出和垃圾收集後不GC.WaitForPendingFinalizers()

回答

12

編譯器和運行時都不需要保證超出範圍的本地實際上已經截斷了其內容的生命週期。出於計算壽命的目的,對於編譯器或運行時來說,對待它似乎不存在大括號是完全合法的。如果您需要基於大括號的清理,然後實施IDisposable並使用「使用」塊。

UPDATE:

關於你的問題:「這是爲什麼不同的優化VS未經優化的建立」,那麼,看看代碼生成的差異。

未優化:

.method private hidebysig static void Main() cil managed 
{ 
    .entrypoint 
    // Code size  28 (0x1c) 
    .maxstack 1 
    .locals init (class test.Foo V_0) 
    IL_0000: nop 
    IL_0001: nop 
    IL_0002: newobj  instance void test.Foo::.ctor() 
    IL_0007: stloc.0 
    IL_0008: nop 
    IL_0009: call  void [mscorlib]System.GC::Collect() 
    IL_000e: nop 
    IL_000f: call  void [mscorlib]System.GC::WaitForPendingFinalizers() 
    IL_0014: nop 
    IL_0015: call  string [mscorlib]System.Console::ReadLine() 
    IL_001a: pop 
    IL_001b: ret 
} // end of method Program::Main 

優化:

.method private hidebysig static void Main() cil managed 
{ 
    .entrypoint 
    // Code size  23 (0x17) 
    .maxstack 8 
    IL_0000: newobj  instance void test.Foo::.ctor() 
    IL_0005: pop 
    IL_0006: call  void [mscorlib]System.GC::Collect() 
    IL_000b: call  void [mscorlib]System.GC::WaitForPendingFinalizers() 
    IL_0010: call  string [mscorlib]System.Console::ReadLine() 
    IL_0015: pop 
    IL_0016: ret 
} // end of method Program::Main 

顯然,一個巨大的差異。顯然,在未優化的版本中,引用存儲在本地插槽0中,直到方法結束纔會刪除。因此,在方法結束之前,GC不能回收內存。在經過優化的版本中,引用存儲在堆棧中,立即從堆棧彈出,並且GC可以自由回收它,因爲堆棧上沒有有效的引用。

+0

好的。這解釋了很多。謝謝。 – 2009-11-11 16:42:12

+0

極好的更新到您的文章,埃裏克。非常感謝! – 2009-11-11 17:03:28

3

使得完全沒有保證何時數據將集。這就是爲什麼您需要使用using聲明來處理一次性物品的原因之一。

GC.WaitForPendingFinalizers()僅等待已收集的終結器 - 如果一個對象尚未收集,它什麼也不做。

儘管您不再有權訪問該名稱,但編譯器很可能會保留一個指向酒吧的指針。

我會嘗試在一個單獨的函數中調用新的Foo(),這可能會幫助,雖然再次 - 沒有保證。

2

欄仍是在範圍上,當你調用GC.Collect()GC.WaitForPendingFinalizers()

富還沒有實現IDisposable()

我的猜測是GC尚未準備好釋放Foo對象正在使用的內存,而且您不能明確地調用Dispose()。因此,它在應用程序完成其執行時被丟棄。

+0

在我實施處置後,Foo確實收到了垃圾,當我期望它的時候。謝謝。 – 2009-11-11 16:48:35

0

我不認爲範圍的工作方式與C++相同。我覺得變量實際上是有效的,直到函數退出,如:

class Program 
{ 
    class Foo 
    { 
     ~Foo() { Console.WriteLine("Test"); } 
    } 


    static void Test() 
    { 
     Foo foo = new Foo(); 
    } 

    static void Main() 
    { 
     Test(); 

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

     Console.ReadLine(); 
    } 
} 

如果你想想IL,那麼有沒有這樣的事情在IL和局部變量梅開二度始終至少有功能範圍。

+0

不確定你的代碼試圖證明什麼。在任何情況下,您都可以使用大括號在C#下強制執行範圍。作爲局部變量,這些可以在同一功能下進行垃圾回收。爲什麼沒有發生在其他職位上解釋。 – 2009-11-11 16:47:48

0

這裏是一個代碼執行意想不到的點可能會出現約GC另一個偉大的文章:

終身,GC.KeepAlive,處理回收 - 由cbrumme http://blogs.msdn.com/b/cbrumme/archive/2003/04/19/51365.aspx?wa=wsignin1.0

我的問題是我怎麼能重現被迫GC在文章中提到的點?我嘗試在OperateOnHandle()的開頭放置GC.Collect(),併爲類C定義了析構函數,但似乎不起作用。析構函數總是在程序結束時被調用。