0

在覆盖 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()

4

5 回答 5

12

编译器和运行时都不需要保证超出范围的本地变量实际上会截断其内容的生命周期。出于计算生命周期的目的,编译器或运行时将其视为大括号不存在是完全合法的。如果您需要基于大括号的清理,则实现 IDisposable 并使用“使用”块。

更新:

关于您的问题“为什么在优化和未优化的构建中会有不同”,那么,看看 codegen 的区别。

未优化:

.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

显然差别很大。显然,在未优化的构建中,引用存储在本地插槽零中,并且在方法结束之前永远不会被删除。因此,在方法结束之前,GC 无法回收内存。在优化的构建中,引用存储在堆栈中,立即从堆栈中弹出,并且 GC 可以自由回收它,因为堆栈上没有剩余的有效引用。

于 2009-11-11T16:34:41.863 回答
3

垃圾收集器绝对不保证何时收集数据。这就是为什么需要使用using声明来处理一次性物品的原因之一。

GC.WaitForPendingFinalizers() 仅等待已收集的终结器 - 如果尚未收集对象,则它什么也不做。

即使您不再有权访问该名称,编译器也很可能会保留指向 bar 的指针。

我会尝试将 new Foo() 的调用放在一个单独的函数中——这可能会有所帮助,尽管再次——不能保证。

于 2009-11-11T16:33:54.897 回答
2

当您调用时 bar 仍在范围GC.Collect()GC.WaitForPendingFinalizers()

Foo 也没有实现IDisposable().

我的猜测是 GC 还没有准备好释放 Foo 对象正在使用的内存,而且你不能显式调用Dispose(). 因此,它会在应用程序完成执行时被释放。

于 2009-11-11T16:34:58.233 回答
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 中没有大括号之类的东西,并且局部变量始终至少具有函数范围。

于 2009-11-11T16:35:31.413 回答
0

这是另一篇关于 GC 可能发生在意外的代码执行点的文章:

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

我的问题是如何在文章中提到的地方重现强制 GC?我试图将 GC.Collect() 放在 OperateOnHandle() 的开头,并为 C 类定义了析构函数,但似乎不起作用。析构函数总是在程序结束时调用。

于 2010-09-19T11:22:19.343 回答