为什么垃圾收集器以错误的顺序获取对象?

我有一个带有两个类A和B的应用程序。类A在类B的引用内部。类的析构函数执行一些资源清理,但它们必须按正确的顺序调用,首先是A的析构函数和那么B的析构函数

发生的事情是,以某种方式首先调用B的析构函数,然后A的析构函数崩溃,因为它试图从已处置的对象执行方法。

GC的这种行为是否正确? 我希望GC检测到A有对B的引用,然后先调用A析构函数。 我对吗?

谢谢你的队友!

PD:如果对析破器/终结器/处理器等有疑问,那就是我们所拥有的:

~A() { this.Dispose(); } ~B() { this.Dispose(); } 

GC是不确定的:它不能保证以任何特定顺序,或在任何时间,甚至永远垃圾收集您的对象 – 如果它想要,它可以决定在程序完成后保留它们。 因此,终结器很少使用或使用。

如果您想要确定性终结,则应使用IDisposable模式 。

正如其他人所说,你的终结者是错误的,错误的,错误的。 你不能简单地在终结器中调用Dispose并期望好事发生。 阅读实施一次性模式的正确方法。

做到这一点是你必须做的工作的开始,而不是结束。 除了这里所有其他正确的答案,我注意到:

  • 终结器可以(通常也可以)在不同的线程上运行。 最终确定与特定线程具有亲缘关系的资源是危险的,如果不小心,可能会遇到死锁,等等。

  • finalization可以通过将对它的引用分配给活动对象中的变量来“复活”死对象。 不要那样做。 令人难以置信的令人困惑。

  • 终结器可以在发生线程中止exception时部分构造的对象上运行。 您不能假设任何对于完全构造的对象都是正确的不变量对于正在最终确定的对象都是正确的。

出于所有这些原因,正确编写终结器非常困难。 避免,避免,避免。

来自http://msdn.microsoft.com/en-us/magazine/bb985010.aspx :

运行时不会对调用Finalize方法的顺序做出任何保证。 例如,假设有一个对象包含指向内部对象的指针。 垃圾收集器检测到两个对象都是垃圾。 此外,假设首先调用内部对象的Finalize方法。 现在,允许外部对象的Finalize方法访问内部对象并在其上调用方法,但内部对象已经完成,结果可能是不可预测的。 因此,强烈建议Finalize方法不要访问任何内部成员对象。

你不应该编写依赖于C#中的解构器的代码,它们可能永远不会被运行,所以不能依赖它们。

编写终结器的正确方法是不要在相关对象上调用任何东西,你应该只通过终结器来销毁非托管资源,正如你所指出的那样,事情是乱序发生的。

正如其他人所指出的那样,这是作为规范的一部分记录的,因此这不是一个错误,也不会改变为“做正确的事”。 特别是,考虑两个对象都具有对另一个的引用,运行时将无法确定这里的正确顺序。

无论如何,这是你应该如何实现终结器。 注意,如果你需要销毁非托管资源,你应该只实现一个终结器(注意,有一些边缘情况你可能想要一个非常特殊用途的类的终结器,但我不会在这里提到这些除此之外):

 public class Test : IDisposable { ~Test() { Dispose(false); } public void Dispose() { Dispose(true); } protected virtual void Dispose(bool disposing) { // TODO: Make sure calling Dispose twice is safe if (disposing) { // call Dispose method on other objects GC.SuppressFinalize(this); } // destroy unmanaged resources here } } 

您可以在此处找到更多信息: 实现Dispose方法 。