2009-11-11 39 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定义了析构函数,但似乎不起作用。析构函数总是在程序结束时被调用。