假设我有这个类:
class Test
{
readonly object _child = new Object();
// ...
~Test()
{
// access _child here
// ...
}
}
_child
对象在垃圾收集器调用 ~Test
时是否保证仍然存活?或者我应该在构造函数中使用 GCHandle.Alloc
先 "pin" 住 _child
吗?
假设我有这个类:
class Test
{
readonly object _child = new Object();
// ...
~Test()
{
// access _child here
// ...
}
}
_child
对象在垃圾收集器调用 ~Test
时是否保证仍然存活?或者我应该在构造函数中使用 GCHandle.Alloc
先 "pin" 住 _child
吗?
_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.Enter
和Monitor.Exit
只关心引用,所以仅用于锁定是绝对安全的。
如果你需要在
Test
的终结器被调用后再调用子类的终结器呢?
有一个解决方法:你可以将Child
类从SafeHandle
中继承,这样就可以解决问题了。它会确保如果Test
和Child
同时超出范围,它将首先调用Test
的终结器,因为Child
继承自SafeHandle
,它会延迟其终结器。但是,在我看来,不要依赖于这一点。因为与你一起工作的其他程序员可能不知道这一点,从而导致误解。
这个关键的终结器还有一个弱排序保证,即如果一个普通可终结对象和一个关键可终结对象同时变得不可达,则首先运行普通对象的终结器
在对象终结期间,任何可达的(子级或外部)对象仍将在内存中可用,但这样一个对象的状态取决于该对象是否已经自身完成了终结——因为在终结队列中没有特定的终结顺序。
简而言之,引用帖子中的话:
您无法访问您的对象引用的任何具有终结器的对象,因为您不能保证这些对象在最终器运行时处于可用状态。对象仍将存在于内存中,未被收集,但它们可能已关闭、终止、终结等。
然而,在对象的终结阶段中,您可以安全使用任何其他可达对象(不具有终结),例如字符串。从实际角度来看,这意味着可以安全地使用任何不实现IDisposable接口的可达对象。
_child
在Test
类之外有被引用吗? - HeinziTest
内部的。 - avo_child
是不安全的。_child
可能在Test
之前被垃圾回收。 - Heinzi_child
在一个方法中被引用,该方法从终结器中调用以访问未托管的资源。该方法会锁定(_child) { ... }
。 - avo