我在类WeakReference<T>
中使用Foo
(short weak reference)跟踪对象。此类具有析构函数,我需要在该析构函数中访问该跟踪的对象。我跟踪的对象也使用Foo
跟踪WeakReference<Foo>
。
所以现在我想知道WeakReference
的“清零”到底何时发生?是在运行任何终结器之前,所有WeakReference
都为空,还是在它们要跟踪的对象的终结器即将运行之前,它们中的每一个都为空?
更新
现在,我还想知道Mono
项目是否可以对此有所启发(link 1,link 2)。但是我有点担心MS GC
和Mono GC
可能以不同的方式处理此问题并且不兼容。
最佳答案
我想写一个演示这个区别的演示程序。事实证明,这比我指望的要更具挑战性。首先,必须确保终结器线程的速度可以放慢,这样您就可以观察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();
}
}
您必须运行此程序,以便调试器不会影响对象的生存期。选择“发布版本”并更改调试器设置:“工具+选项”,“调试”,“常规”,取消选中“抑制JIT优化”选项。
事实证明,对象的最终确定顺序确实是不确定的。每次您运行该程序的顺序都不同。我们希望FinalizerDelayer对象首先被完成,但这并不总是会发生。我认为这是内置地址空间布局随机化功能的副作用,它使托管代码很难受到攻击。但是经常运行它,您最终会得到:
长话短说:
当对象恢复时要当心怪癖,当重新创建强引用时,会将对象从易碎队列移回普通堆。无需我在此演示程序中进行探索,但需要很长的弱引用才能观察到。您需要长期的薄弱引用的基本原因。