【问题标题】:Are child objects still alive when Object.Finalize is called by GC?当 GC 调用 Object.Finalize 时,子对象是否仍然存在?
【发布时间】:2014-10-18 06:55:10
【问题描述】:

说,我有这门课:

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

    // ...

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

当垃圾收集器调用~Test 时,_child 对象是否保证仍然存在?还是我应该先在构造函数中用GCHandle.Alloc“固定”_child

【问题讨论】:

  • Test类之外的任何地方都引用了_child吗?
  • @Heinzi,不,它纯粹是 Test 内部的。
  • 基于this question 我不认为使用_child 是安全的。 _child 可能在 Test 之前被垃圾收集。
  • 好的,我刚刚做了测试:stackoverflow.com/a/25480821/2674222?
  • 我认为安全性取决于您想对子对象做什么?

标签: c# .net garbage-collection finalizer


【解决方案1】:

作为readonly 字段_child,您不能丢失其引用(除非您通过反射将其设置为空)。这意味着在 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 实例不会为空,这意味着它指向一个有效的引用。 Monitor.EnterMonitor.Exit 只关心引用,使用它绝对安全(仅用于锁定)。

如果您只需要在之后调用子项的终结器怎么办 Test's 终结器被调用?

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

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

引用SafeHandle: A Reliability Case Study

【讨论】:

  • 看来这是错误的,还是我遗漏了什么:stackoverflow.com/a/25480821/2674222
  • @avo 更新了我的答案,看看是否有帮助
  • 我在新版@SriramSakthivel 中接受了这个答案。
  • @avo 您的后续链接导致递归。无论如何I got it :)
【解决方案2】:

到目前为止,恕我直言,最充分的解释可以在 here 找到(请参阅 Karlsen 的回答),因此总结对 OP 的回答:

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

简而言之,引用帖子:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-03-20
    • 1970-01-01
    • 1970-01-01
    • 2020-10-12
    • 2013-11-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多