虽然'调查'最终确定(阅读:尝试愚蠢的事情)我偶然发现了一些意想不到的行为(至少对我来说)。
我原本期望不会调用Finalize方法,而是调用它两次
class Program
{
static void Main(string[] args)
{
// The MyClass type has a Finalize method defined for it
// Creating a MyClass places a reference to obj on the finalization table.
var myClass = new MyClass();
// Append another 2 references for myClass onto the finalization table.
System.GC.ReRegisterForFinalize(myClass);
System.GC.ReRegisterForFinalize(myClass);
// There are now 3 references to myClass on the finalization table.
System.GC.SuppressFinalize(myClass);
System.GC.SuppressFinalize(myClass);
System.GC.SuppressFinalize(myClass);
// Remove the reference to the object.
myClass = null;
// Force the GC to collect the object.
System.GC.Collect(2, System.GCCollectionMode.Forced);
// The first call to obj's Finalize method will be discarded but
// two calls to Finalize are still performed.
System.Console.ReadLine();
}
}
class MyClass
{
~MyClass()
{
System.Console.WriteLine("Finalise() called");
}
}
有人可以解释这种行为是否是故意的,为什么会这样?
以上代码是在x86调试模式下编译并在CLR v4上运行。
非常感谢
答案 0 :(得分:16)
我不知道造成这种怪异行为的原因。但是,由于您违反了该方法的记录用法,因此可能发生任何事情。 ReRegisterForFinalize的文档说:
请求系统调用先前已调用SuppressFinalize的指定对象的终结器。
在调用ReRegisterForFinalize之前,您之前没有调用SuppressFinalize。文档没有说明在那种情况下会发生什么,事实上,显然发生了一些非常奇怪的事情。
不幸的是,相同的文档页面继续显示一个示例,其中在尚未调用SuppressFinalize的对象上调用ReRegisterForFinalize。
这有点乱。我将与文档管理员联系。
当然,如果你违反文件中描述的规则,那么这个故事的道德就是伤害,然后停止违反它们。
答案 1 :(得分:10)
我可以猜测 ......这真的只是猜测。正如埃里克所说,不要违反这样的规则:)这个猜测只是为了闲置的猜测和兴趣。
我怀疑涉及两种数据结构:
当GC注意到一个对象符合垃圾收集条件时,我怀疑它会检查对象的头并将引用添加到终结队列。您对SuppressFinalization
的来电阻止了这种行为。
另外,终结器线程遍历终结队列并为找到的所有内容调用终结器。您对ReRegisterForFinalize
的调用绕过了引用最终在队列中的正常方式,并直接添加它。 SuppressFinalization
不会从队列中删除引用 - 它只是阻止引用以正常方式添加到队列中。
所有这些都可以解释你所看到的行为(以及我所复制的行为)。它还解释了为什么当我删除SuppressFinalization
调用时,我最终看到终结器名为三次 - 因为在这种情况下,“正常”路径也会将引用添加到终结队列中。
答案 2 :(得分:5)
有趣的数据点:
Win32上的mono 2.6.7不会调用终结器
Win32上的.NET 3.5调用终结器两次
测试代码供参考:
class Program
{
static void Main(string[] args)
{
// The MyClass type has a Finalize method defined for it
// Creating a MyClass places a reference to obj on the finalization table.
var myClass = new MyClass();
// Append another 2 references for myClass onto the finalization table.
System.GC.ReRegisterForFinalize(myClass);
System.GC.ReRegisterForFinalize(myClass);
// There are now 3 references to myClass on the finalization table.
System.GC.SuppressFinalize(myClass);
System.GC.SuppressFinalize(myClass);
System.GC.SuppressFinalize(myClass);
// Remove the reference to the object.
myClass = null;
// Force the GC to collect the object.
System.GC.Collect(2, System.GCCollectionMode.Forced);
// The first call to obj's Finalize method will be discarded but
// two calls to Finalize are still performed.
}
}
class MyClass
{
~MyClass()
{
System.Console.WriteLine("Finalise() called");
}
}
答案 3 :(得分:2)