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

时间:2011-11-30 12:31:32

标签: c# .net delegates garbage-collection

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

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

我尝试构建一个单元测试来验证这一点,但它似乎没有按照我计划的方式运行,而是看起来我对WeakReference的假设(我在这里用于测试目的) ,或其他一些假设,没有水。

请查看此代码,您可以在LINQPad

中运行该代码
void Main()
{
    WeakReference wr;
    Lazy<int> 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<int> l)
{
    Func<int> f = () => 10;
    wr = new WeakReference(f);
    l = new Lazy<int>(f);
}

public class Lazy<T>
{
    private Func<T> _GetValue;
    private T _Value;

    public Lazy(Func<T> getValue)
    {
        _GetValue = getValue;
    }

    public T Value
    {
        get
        {
            if (_GetValue != null)
            {
                _Value = _GetValue();
                _GetValue = null;
            }
            return _Value;
        }
    }
}

我假设:

  1. 无论DEBUG构建,附加调试器,因为我在一个单独的方法中创建了委托,我从中返回,除了WeakReferenceLazy<T>之外,应该没有任何内容保留委托。对象
  2. 如果我要求Lazy<T>对象放弃对该委托的引用,这会减少仅引用WeakReference所持有的对象
  3. 然后强制进行完整的垃圾收集,假设剩下的唯一引用是WeakReference
  4. 中的引用
  5. 然后收集代表,我的WeakReference表示该对象不再存在
  6. 因此预期代码的输出(带注释):

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

    但输出是:

    true
    true
    10
    true
    

    有人能说清楚我在这里缺少什么吗?

1 个答案:

答案 0 :(得分:6)

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

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

变成了类似的东西:

static Func<int> hiddenDelegate;

static int HiddenMethod()
{
    return 10;
}

void CreateTestData(out WeakReference wr, out Lazy<int> l)
{
    if (hiddenDelegate == null)
    {
        hiddenDelegate = HiddenMethod;
    }

    Func<int> f = hiddenDelegate;
    ...
}

查看ildasm(或未启用优化的Reflector)中的代码,看看究竟发生了什么。