我计划写一个小的演示程序来展示差异。结果比我想象的要具有挑战性。第一个必要条件是确保终结器线程可以减慢速度,这样您就可以观察到WeakReference.IsAlive的值,而不必担心它受到与终结器线程竞争的影响。因此我使用了:
class FinalizerDelayer {
~FinalizerDelayer() {
Console.WriteLine("Delaying finalizer...");
System.Threading.Thread.Sleep(500);
Console.WriteLine("Delay done");
}
}
接下来是一个将作为WeakReference目标的小类:
class Example {
private int instance;
public Example(int instance) { this.instance = instance; }
~Example() {
Console.WriteLine("Example {0} finalized", instance);
}
}
class Program {
static void Main(string[] args) {
var target1 = new Example(1);
var target2 = new Example(2);
var shortweak = new WeakReference(target1);
var longweak = new WeakReference(target2, true);
var delay = new FinalizerDelayer();
GC.Collect(); // Kills short reference
Console.WriteLine("Short alive = {0}", shortweak.IsAlive);
Console.WriteLine("Long alive = {0}", longweak.IsAlive);
GC.WaitForPendingFinalizers();
Console.WriteLine("Finalization done");
GC.Collect(); // Kills long reference
Console.WriteLine("Long alive = {0}", longweak.IsAlive);
Console.ReadLine();
}
}
WeakReference
类型本身的文档更加清晰明了。trackResurrection
。参数的描述如下:
“备注”部分还写道:指示何时停止跟踪对象。如果为 true,则在终结后跟踪对象;如果为 false,则仅在终结前跟踪对象。
这证实了当您使用“短”弱引用时,已终结的对象将不再被如果 trackResurrection 为 false,则创建短弱引用。如果 trackResurrection 为 true,则创建长弱引用。
WeakReference
对象跟踪(即Target
变为null
),但当您使用“长”弱引用时,它将被跟踪。Target
属性设置为null
的确切时刻似乎无关紧要。如果程序中的某个其他线程观察到该值为非空,则最终器尚未运行。如果它观察到它为空,则最终器已经运行。那个“其他线程”不应该在最终器线程工作时自己运行,因此对于那个“其他线程”而言,终结基本上是原子的。垃圾收集器构建所有可达对象的图形。本文第一部分讨论了如何完成此操作。
垃圾收集器扫描短弱引用表。如果表中指针所引用的对象不属于该图形,则该指针标识一个不可达对象,而短弱引用表中的插槽被设置为 null。
垃圾收集器扫描终结队列。如果队列中的指针引用的对象不属于图形,则该指针标识一个不可达对象,并将该指针从终结队列移动到 freachable 队列。此时,由于对象现在被认为是可达的,因此将对象添加到图形中。
垃圾收集器扫描长弱引用表。如果表中指针所引用的对象不属于该图形(该图形现在包含 freachable 队列中条目所指向的对象),则该指针标识一个不可达对象,插槽被设置为 null。
垃圾收集器压缩内存,挤出不可达对象留下的空洞。
所以,即使我的类Foo
有一个finalizer,因此它将在freachable队列中(被认为是根) - 短弱引用的解析发生在此对象在freachable队列中成为根之前,这意味着短弱引用将为空:
垃圾收集器一旦确定对象不可达就会在短弱引用表中将指针设置为null。如果对象具有Finalize方法,则该方法尚未被调用,因此对象仍然存在。如果应用程序访问WeakReference对象的Target属性,则即使对象实际上仍然存在,也将返回null。
WeakReference
是一个例外(尽管这并不总是这样)。由于WeakReference
是一个带有finalizer的对象,如果它在Foo
的finalizer中被访问,它可能已经被finalize了(在这种情况下,Target
属性将始终返回null,尽管跟踪的对象可能仍然存活良好(更多信息)。
我刚刚确认Mono垃圾回收器与此行为一致。
有用的参考资料:
- WeakReference 源代码
- WeakReference<T> 源代码
Target
getter内发现了一个有趣的注释:“只有在非法使用时才会发生,例如从终结器中使用WeakReference” - 所以也许你正在尝试做的事情注定会失败。 - Damien_The_UnbelieverWeakReference
指向真实对象,但由于这些对象已被回收,所以WeakReference.Target
现在为空。然而,WeakReference
对象仍然存在于字典中,浪费空间。 - Paya