简体   繁体   English

Object.Finalize()重写和GC.Collect()

[英]Object.Finalize() override and GC.Collect()

I cannot seem to understand the behavior of GC.Collect() under the presence of a class overriding Object.Finalize(). 在覆盖Object.Finalize()的类的存在下,我似乎无法理解GC.Collect()的行为。 This is my base code: 这是我的基本代码:

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();
  }
 }

}

Contrary to what I was expecting, I only get the console output at program termination and not after GC.WaitForPendingFinalizers() 与我期望的相反,我仅在程序终止时获得控制台输出,而在GC.WaitForPendingFinalizers()之后GC.WaitForPendingFinalizers()未获得控制台输出。

Neither the compiler nor the runtime are required to guarantee that locals that are out of scope actually have the lifetimes of their contents truncated. 不需要编译器和运行时来保证超出范围的本地实际上将其内容的生存期缩短。 It is perfectly legal for the compiler or the runtime to treat this as though the braces were not there, for the purposes of computing lifetime. 出于计算生命周期的目的,对于编译器或运行时,将其视为大括号不存在是完全合法的。 If you require brace-based cleanup, then implement IDisposable and use the "using" block. 如果需要基于大括号的清理,则实现IDisposable并使用“ using”块。

UPDATE: 更新:

Regarding your question "why is this different in optimized vs unoptimized builds", well, look at the difference in codegen. 关于您的问题“为什么优化后的版本与未优化的版本有何不同”,请看一下代码生成中的差异。

UNOPTIMIZED: 未优化:

.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

OPTIMIZED: 优化:

.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

Obviously a massive difference. 显然是巨大的差异。 Clearly in the unoptimized build, the reference is stored in local slot zero and never removed until the method ends. 显然,在未优化的构建中,引用存储在本地插槽0中,并且直到方法结束才删除。 Therefore the GC cannot reclaim the memory until the method ends. 因此,在方法结束之前,GC无法回收内存。 In the optimized build, the reference is stored on the stack, immediately popped off the stack, and the GC is free to reclaim it since there is no valid reference left on the stack. 在优化的版本中,引用被存储在堆栈中,并立即从堆栈中弹出,并且由于堆栈上没有有效的引用,GC可以自由地对其进行回收。

The garbage collector makes absolutely no guarantee as to when data will be collected. 垃圾收集器绝对不能保证何时收集数据。 This is one of the reasons why you need to use the using statement for disposing disposable objects. 这就是为什么您需要使用using语句来处理一次性对象的原因之一。

GC.WaitForPendingFinalizers() only waits for finalizers which have been collected - if an object wasn't yet collected it does nothing. GC.WaitForPendingFinalizers()仅等待已收集的终结器-如果尚未收集对象,则不执行任何操作。

Most likely the compiler is keeping a pointer to bar around even though you don't have access to the name anymore. 即使您不再有权使用该名称,编译器也很可能会保留指向该指针的指针。

I would try putting the call to new Foo() in a separate function - that might help, although again - no guarantee. 我会尝试将对新Foo()的调用放在一个单独的函数中-可能会有所帮助,尽管同样-无法保证。

bar is still in scope when you call GC.Collect() and GC.WaitForPendingFinalizers() 当您调用GC.Collect()GC.WaitForPendingFinalizers()时,bar仍在作用域内

Foo also doesn't implement IDisposable() . Foo也没有实现IDisposable()

My guess is that the GC isn't ready to free up the memory in use by your Foo object yet, and you can't explicitly call Dispose() . 我的猜测是,GC尚未准备好释放Foo对象正在使用的内存,并且您无法显式调用Dispose() Therefore, it's getting disposed when the application completes its execution. 因此,当应用程序完成执行时,它会被销毁。

I don't think scoping works the same way as in C++. 我认为范围界定的工作方式与C ++不同。 I think the variables are actually valid until function exit, eg: 我认为在函数退出之前变量实际上是有效的,例如:

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();
    }
}

If you think about the IL, then there is no such thing as a brace in IL and local variables always have at least function scope. 如果您考虑一下IL,那么IL中就没有括号,局部变量始终至少具有函数作用域。

Here is another great article about GC may occur at unexpected point of code execution: 这是另一篇有关GC的精彩文章,可能在代码执行的意外点发生:

Lifetime, GC.KeepAlive, handle recycling - by cbrumme http://blogs.msdn.com/b/cbrumme/archive/2003/04/19/51365.aspx?wa=wsignin1.0 生命周期(GC.KeepAlive)处理回收-通过cbrumme http://blogs.msdn.com/b/cbrumme/archive/2003/04/19/51365.aspx?wa=wsignin1.0

My question is how can I reproduce forced GC at the point mentioned in the article? 我的问题是如何在文章中提到的点重现强制GC? I tried to put GC.Collect() at beginning of OperateOnHandle(), and defined destructor for class C but seems not working. 我试图将GC.Collect()放在OperateOnHandle()的开头,并为类C定义了析构函数,但似乎不起作用。 Destructor is invoked always at end of program. 析构函数总是在程序结束时调用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM