的子对象是否仍然存活.Finalize由GC调用
本文关键字:Finalize GC 调用 对象 是否 | 更新日期: 2023-09-27 18:19:21
比如说,我有这样一个类:
class Test
{
readonly object _child = new Object();
// ...
~Test()
{
// access _child here
// ...
}
}
当垃圾收集器调用~Test
时,_child
对象是否保证仍然是活的?或者我应该首先"pin"_child
与GCHandle.Alloc
在构造函数中?
作为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.Enter
和Monitor.Exit
只关心引用,使用它是绝对安全的(仅用于锁定)。
如果你需要子类的终结器只在
Test's
终结器被调用?
有一个解决方法:您可以从SafeHandle
继承Child
类,这样就可以解决问题了。它将确保如果Test
和Child
同时超出作用域,它将首先调用Test's
终结器,因为Child
继承自SafeHandle
,这会延迟其终结。但是,我不依赖于此。因为与您一起工作的其他程序员可能不知道这一点,从而导致误解。
这个临界终结器也有一个弱排序保证,声明如果一个普通可终结对象和一个临界可终结对象同时不可达,那么普通对象的终结器将首先运行
引用自SafeHandle:可靠性案例研究
到目前为止,我可以在这里找到最充分的解释(参见Karlsen的回答),所以总结一下对OP的回答:
在对象结束过程中,任何可访问的(子对象或外部)对象仍将在内存中可用,但此类对象的状态将取决于该对象本身是否已经结束-因为在结束队列中对象之间没有特定的结束顺序。
总之,引用原文:
然而,你可以在对象的结束阶段安全地使用任何其他可访问的对象(没有结束),例如字符串。实际上,这意味着使用任何不实现IDisposable接口的可访问对象都是安全的。你不能访问你的对象所引用的任何有终结器的对象,因为你不能保证当你的终结器运行时这些对象将处于可用状态。对象仍然在那里,在内存中,没有被收集,但是它们可能已经被关闭、终止、最终确定等。