什么时候短的弱引用变为空?

Pay*_*aya 8 c# garbage-collection memory-management weak-references

我在班上使用WeakReference<T>(短弱参考)跟踪一个对象Foo.这个类有一个析构函数,我需要在其中访问该跟踪对象.我跟踪的对象也在跟踪Foo使用WeakReference<Foo>.

所以现在我想知道,究竟什么时候"归零"了WeakReference呢?WeakReference在任何终结器运行之前,所有这些都会被取消,或者在它们跟踪的对象的终结器即将运行之前,它们是否都被取消了?

UPDATE

现在我也想知道是否Mono项目可以为这个项目提供一些启示(链接1,链接2).但我有点担心,也许MS GC并且Mono GC可能会以不同的方式解决这个问题而且不相容.

Han*_*ant 5

我想写一个演示程序来演示差异.原来比我指望的更具挑战性.第一个必要的成分是确保终结器线程可以减慢速度,这样你就可以观察到WeakReference.IsAlive的值,而不会有被终结器线程影响的风险.所以我用过:

class FinalizerDelayer {
    ~FinalizerDelayer() {
        Console.WriteLine("Delaying finalizer...");
        System.Threading.Thread.Sleep(500);
        Console.WriteLine("Delay done");
    }
}
Run Code Online (Sandbox Code Playgroud)

然后是一个将成为WeakReference目标的小类:

class Example {
    private int instance;
    public Example(int instance) { this.instance = instance; }
    ~Example() {
        Console.WriteLine("Example {0} finalized", instance);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后是一个程序,它演示了长弱引用之间的区别:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

您必须运行此程序,以便调试器不会影响对象的生命周期.选择Release build并更改调试器设置:Tools + Options,Debugging,General,取消选中"Suppress JIT optimization"选项.

原来对象的终结顺序确实是非确定性的.每次运行程序时,顺序都不同.我们希望FinalizerDelayer对象首先完成,但并不总是这样.我认为这是内置地址空间布局随机化功能的副作用,它使托管代码很难攻击.但经常运行它,你最终会得到:

延迟终结器...
短活着=假
长活=
完成延迟
示例1已完成
示例2已完成终结已
完成
Long alive = False

长话短说:

  • 只要收集对象并将其放置在可释放队列上,就可以将IsAlive设置为false,以便最终确定它.该对象仍然存在,但不再存在强引用,它很快就会完成.
  • 长弱引用会在整个实际生命周期中跟踪对象,包括它在可释放队列中的生命周期.在终结器完成之前,IsAlive不会设置为false.

当对象复活时要小心一个怪癖,当重新创建一个强引用时,将其从可释放队列移回正常堆.这不是我在这个演示程序中探索过的东西,但需要一个很长的弱引用来观察它.你需要长期弱参考的基本原因.


Pet*_*iho 4

您可以通过一个简单的测试程序自行验证。但我发现类型本身的文档WeakReference比您正在查看的页面更清晰。

特别是,链接页面中称为“短”和“长”的标志trackResurrection实际的构造函数文档中被调用。该参数的描述如下:

指示何时停止跟踪对象。如果为 true,则在完成后跟踪对象;如果为 false,则仅跟踪对象直到最终确定。

“备注”部分还写道:

如果 trackResurrection 为 false,则会创建一个短弱引用。如果 trackResurrection 为 true,则会创建一个长弱引用。

这证实了当您使用“短”弱引用时,最终确定的对象将不再被该对象跟踪(即成为TargetnullWeakReference但是当您使用“长”弱引用时,它就会被跟踪。

对于这两种弱引用,实际上已被垃圾收集的对象肯定不会再被跟踪(显然)。

一般来说,当终结器线程实际执行其工作时,程序中的其他线程不应该能够观察到对象,因此当属性Target设置为“短”弱引用时的精确时刻null对我来说似乎没有意义。如果程序中的其他线程观察到该值为非空,则终结器尚未运行。如果它观察到它为 null,则终结器已运行。当终结器线程工作时,“其他线程”本身不应该运行,因此就“其他线程”而言,终结本质上应该是原子的。