当GC调用Object.Finalize时,子对象是否仍然存在?

4

假设我有这个类:

class Test
{
    readonly object _child = new Object();

    // ...

    ~Test()
    {
        // access _child here
        // ...
    }
}

_child 对象在垃圾收集器调用 ~Test 时是否保证仍然存活?或者我应该在构造函数中使用 GCHandle.Alloc 先 "pin" 住 _child 吗?


_childTest 类之外有被引用吗? - Heinzi
@Heinzi,不,这纯粹是Test内部的。 - avo
根据这个问题,我认为使用_child是不安全的。 _child可能在Test之前被垃圾回收。 - Heinzi
通常,如果在对象最终化之前需要执行某些操作,则应实现IDispose并确保引用代码始终处置该对象,一个好的方法是将其包含在using语句中。我不会使用GCHandle。 - Mick
@Mick,_child 在一个方法中被引用,该方法从终结器中调用以访问未托管的资源。该方法会锁定 (_child) { ... } - avo
显示剩余8条评论
2个回答

2
作为一个只读字段,_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.
    }
}

在处理托管代码时,几乎不需要使用终结器。它会给垃圾回收器增加额外的工作量,并且像上面看到的那样可能会发生奇怪的事情。

更新:如果你只是用_child来进行lock操作,那么它是安全的,因为_child实例不会为null,这意味着它指向一个有效的引用。 Monitor.EnterMonitor.Exit只关心引用,所以仅用于锁定是绝对安全的。

如果你需要在Test的终结器被调用后再调用子类的终结器呢?

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

这个关键的终结器还有一个弱排序保证,即如果一个普通可终结对象和一个关键可终结对象同时变得不可达,则首先运行普通对象的终结器

引用自SafeHandle: A Reliability Case Study


看起来这个不对,或者我漏掉了什么:http://stackoverflow.com/a/25480821/2674222? - avo
@avo 我更新了我的答案,看看是否有帮助。 - Sriram Sakthivel
我接受了@SriramSakthivel的新版本答案。 - avo
1
@avo,你的跟进链接导致了递归。不管怎样,我已经明白了 :)。链接:https://dev59.com/Q4Lba4cB1Zd3GeqPjMn8 - Sriram Sakthivel

2
到目前为止,我认为最充分的解释可以在这里找到(请参见Karlsen的答案)。总结对OP的答案:

在对象终结期间,任何可达的(子级或外部)对象仍将在内存中可用,但这样一个对象的状态取决于该对象是否已经自身完成了终结——因为在终结队列中没有特定的终结顺序。

简而言之,引用帖子中的话:

您无法访问您的对象引用的任何具有终结器的对象,因为您不能保证这些对象在最终器运行时处于可用状态。对象仍将存在于内存中,未被收集,但它们可能已关闭、终止、终结等。

然而,在对象的终结阶段中,您可以安全使用任何其他可达对象(不具有终结),例如字符串。从实际角度来看,这意味着可以安全地使用任何不实现IDisposable接口的可达对象。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接