本文介绍了垃圾收集应该已删除对象,但 WeakReference.IsAlive 仍返回 true的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个我希望通过的测试,但垃圾收集器的行为并不像我想象的那样:

I have a test that I expected to pass but the behavior of the Garbage Collector is not as I presumed:

[Test]
public void WeakReferenceTest2()
{
    var obj = new object();
    var wRef = new WeakReference(obj);

    wRef.IsAlive.Should().BeTrue(); //passes

    GC.Collect();

    wRef.IsAlive.Should().BeTrue(); //passes

    obj = null;

    GC.Collect();

    wRef.IsAlive.Should().BeFalse(); //fails
}

在本例中,obj 对象应该是 GC'd,因此我希望 WeakReference.IsAlive 属性返回 false.

In this example the obj object should be GC'd and therefore I would expect the WeakReference.IsAlive property to return false.

似乎因为 obj 变量被声明在与 GC.Collect 相同的范围内,所以它没有被收集.如果我将 obj 声明和初始化移到测试通过的方法之外.

It seems that because the obj variable was declared in the same scope as the GC.Collect it is not being collected. If I move the obj declaration and initialization outside of the method the test passes.

是否有人对此行为有任何技术参考文档或解释?

Does anyone have any technical reference documentation or explanation for this behavior?

推荐答案

遇到和你一样的问题 - 我的测试到处都通过了,除了在 NCrunch 下(在你的情况下可能是任何其他仪器).嗯.使用 SOS 进行调试会发现在测试方法的调用堆栈上保留了额外的根.我的猜测是,它们是禁用任何编译器优化的代码检测的结果,包括那些正确计算对象可达性的优化.

Hit the same issue as you - my test was passing everywhere, except for under NCrunch (could be any other instrumentation in your case). Hm. Debugging with SOS revealed additional roots held on a call stack of a test method. My guess is that they were a result of code instrumentation that disabled any compiler optimizations, including those that correctly compute object reachability.

这里的解决方法很简单 - 永远不要持有来自执行 GC 和测试活动性的方法的强引用.这可以通过简单的辅助方法轻松实现.下面的更改使您的测试用例在 NCrunch 中通过,而它最初是失败的.

The cure here is quite simple - don't ever hold strong references from a method that does GC and tests for aliveness. This can be easily achieved with a trivial helper method. The change below made your test case pass with NCrunch, where it was originally failing.

[TestMethod]
public void WeakReferenceTest2()
{
    var wRef2 = CallInItsOwnScope(() =>
    {
        var obj = new object();
        var wRef = new WeakReference(obj);

        wRef.IsAlive.Should().BeTrue(); //passes

        GC.Collect();

        wRef.IsAlive.Should().BeTrue(); //passes
        return wRef;
    });

    GC.Collect();

    wRef2.IsAlive.Should().BeFalse(); //used to fail, now passes
}

private T CallInItsOwnScope<T>(Func<T> getter)
{
    return getter();
}

这篇关于垃圾收集应该已删除对象,但 WeakReference.IsAlive 仍返回 true的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-29 03:29