代表们的GC,我错过了什么? (我的代表没有收集)

我有一个持有代表的类,以便稍后懒惰地评估一些东西。

一旦我评估了它,通过调用委托,我清除了对委托的引用,希望它有资格收集。 毕竟,如果将它构造为匿名方法,它可能会保留一个局部变量的世界。

我尝试构建一个unit testing来validation这一点,但它似乎没有按照我计划的方式解决,相反,似乎我对WeakReference (我在这里用于测试目的)的假设,或者其他一些假设,保持水。

看看这段代码,你可以在LINQPad中运行它

 void Main() { WeakReference wr; Lazy l; CreateTestData(out wr, out l); wr.IsAlive.Dump(); // should be alive here GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); wr.IsAlive.Dump(); // and alive here as well l.Value.Dump(); // but now we clear the reference GC.Collect(); // so one of these should collect it GC.WaitForPendingFinalizers(); GC.Collect(); wr.IsAlive.Dump(); // and then it should be gone here GC.KeepAlive(l); } void CreateTestData(out WeakReference wr, out Lazy l) { Func f = () => 10; wr = new WeakReference(f); l = new Lazy(f); } public class Lazy { private Func _GetValue; private T _Value; public Lazy(Func getValue) { _GetValue = getValue; } public T Value { get { if (_GetValue != null) { _Value = _GetValue(); _GetValue = null; } return _Value; } } } 

我假设:

  1. 无论DEBUG构建,附加调试器,因为我在一个单独的方法中创建了委托,我从中返回,除了WeakReferenceLazy对象之外,应该没有任何东西保留委托。
  2. 如果我要求Lazy对象放弃对委托的引用,这将减少对WeakReference所持有的WeakReference
  3. 然后强制进行完整的垃圾收集,假设剩下的唯一引用是WeakReference中的WeakReference
  4. 然后将收集委托,我的WeakReference将指示该对象不再存在

因此预计代码的输出(带注释):

 true // not gc'ed after construction true // not gc'ed after full GC, still beind held by Lazy 10 // value from calling delegate false // but is now gc'ed, Lazy no longer has a reference to it 

但相反输出是:

 true true 10 true 

任何人都可以了解我在这里缺少的东西吗?

“问题”是编译器注意到它可以永远重用单个委托实例。 它不捕获任何上下文,甚至不隐含this引用。 所以这:

 void CreateTestData(out WeakReference wr, out Lazy l) { Func f = () => 10; ... } 

变成了这样的东西:

 static Func hiddenDelegate; static int HiddenMethod() { return 10; } void CreateTestData(out WeakReference wr, out Lazy l) { if (hiddenDelegate == null) { hiddenDelegate = HiddenMethod; } Func f = hiddenDelegate; ... } 

查看ildasm(或没有打开优化的Reflector)中的代码,看看究竟发生了什么。