问题描述
我有一个我希望通过的测试,但垃圾收集器的行为并不像我想象的那样:
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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!