的子对象是否仍然存活.Finalize由GC调用

本文关键字:Finalize GC 调用 对象 是否 | 更新日期: 2023-09-27 18:19:21

比如说,我有这样一个类:

class Test
{
    readonly object _child = new Object();
    // ...
    ~Test()
    {
        // access _child here
        // ...
    }
}

当垃圾收集器调用~Test时,_child对象是否保证仍然是活的?或者我应该首先"pin"_childGCHandle.Alloc在构造函数中?

的子对象是否仍然存活.Finalize由GC调用

作为readonly字段_child,您不能丢失其引用(除非通过反射将其设置为null)。这意味着在Test被垃圾收集之前,_child肯定会留在内存中。

同样,您正在使用Finalizer,它在垃圾收集之前被调用,只有在下一次传递对象的内存才会被回收,此时_child将是活的。换句话说,当Finalize方法被调用时,_child将是活动的,并且可以安全地访问它。

Finalizer被调用并不意味着内存会被回收,如果你做了下面的事情,Finalize会被调用,但是内存不会被回收

class Test
{
    readonly object _child = new Object();
    private static Test evilInstance;
    ~Test()
    {
        evilInstance = this;//Do something crazy
        //This resurrects this instance, so memory will not be reclaimed.
    }
}
当你处理托管代码时,几乎不需要

终结器,它给垃圾收集器增加了额外的工作,而且还可能发生我们上面看到的奇怪的事情。

Update:如果你只对lock使用_child是安全的,因为_child实例不会为空,这意味着它指向一个有效的引用。Monitor.EnterMonitor.Exit只关心引用,使用它是绝对安全的(仅用于锁定)。

如果你需要子类的终结器只在Test's终结器被调用?

有一个解决方法:您可以从SafeHandle继承Child类,这样就可以解决问题了。它将确保如果TestChild同时超出作用域,它将首先调用Test's终结器,因为Child继承自SafeHandle,这会延迟其终结。但是,我不依赖于此。因为与您一起工作的其他程序员可能不知道这一点,从而导致误解。

这个临界终结器也有一个弱排序保证,声明如果一个普通可终结对象和一个临界可终结对象同时不可达,那么普通对象的终结器将首先运行

引用自SafeHandle:可靠性案例研究

到目前为止,我可以在这里找到最充分的解释(参见Karlsen的回答),所以总结一下对OP的回答:

在对象结束过程中,任何可访问的(子对象或外部)对象仍将在内存中可用,但此类对象的状态将取决于该对象本身是否已经结束-因为在结束队列中对象之间没有特定的结束顺序。

总之,引用原文:

你不能访问你的对象所引用的任何有终结器的对象,因为你不能保证当你的终结器运行时这些对象将处于可用状态。对象仍然在那里,在内存中,没有被收集,但是它们可能已经被关闭、终止、最终确定等。

然而,你可以在对象的结束阶段安全地使用任何其他可访问的对象(没有结束),例如字符串。实际上,这意味着使用任何不实现IDisposable接口的可访问对象都是安全的。