在覆盖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()
答案 0 :(得分:12)
编译器和运行时都不需要保证超出范围的本地实际上会截断其内容的生命周期。出于计算生命周期的目的,编译器或运行时将其视为不存在大括号是完全合法的。如果需要基于大括号的清理,则实现IDisposable并使用“using”块。
更新:
关于你的问题“为什么这在优化与未经优化的版本中有所不同”,好吧,看看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可以自由回收它,因为堆栈上没有剩余的有效引用。
答案 1 :(得分:3)
垃圾收集器绝对不能保证何时收集数据。这是您需要使用using
语句来处置一次性对象的原因之一。
GC.WaitForPendingFinalizers()仅等待已收集的终结器 - 如果尚未收集对象,则不执行任何操作。
即使您无法再访问该名称,编译器也很可能会保留一个指向bar的指针。
我会尝试将新Foo()的调用放在一个单独的函数中 - 这可能会有所帮助,尽管再次 - 不能保证。
答案 2 :(得分:2)
GC.Collect()
和GC.WaitForPendingFinalizers()
时,栏仍在范围内
Foo也没有实现IDisposable()
。
我的猜测是GC尚未准备好释放Foo对象正在使用的内存,而且您无法明确调用Dispose()
。因此,当应用程序完成执行时它就会被处理掉。
答案 3 :(得分: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中没有支撑这样的东西,局部变量总是至少具有函数范围。
答案 4 :(得分: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定义了析构函数,但似乎无法正常工作。总是在程序结束时调用析构函数。