代表们不会收集垃圾

时间:2014-03-04 13:26:36

标签: c# garbage-collection

以下是一个演示此问题的控制台应用:

class Program
{
    static void Main()
    {
        InitRefs();
        GC.Collect();
        GC.WaitForPendingFinalizers();
        Console.WriteLine(_refObj.IsAlive);
        Console.WriteLine(_refAction.IsAlive);
        Console.WriteLine(_refEvent.IsAlive);
        Console.ReadKey();
    }

    private static void InitRefs()
    {
        _refObj = new WeakReference(new object());
        _refAction = new WeakReference((Action) (() => { }));
        _refEvent = new WeakReference(new EventHandler((sender, eventArgs) => { }));
    }

    private static WeakReference _refObj;
    private static WeakReference _refAction;
    private static WeakReference _refEvent;
}

输出为“False True True”。

我已经使用SOS.dll试图找到让代表们保持GCed的内容,这就是我为Action做的事情:

!gcroot 02472584
HandleTable:
    006613ec (pinned handle)
    -> 03473390 System.Object[]
    -> 02472584 System.Action

有人可以解释发生了什么吗?

1 个答案:

答案 0 :(得分:6)

您的代理人没有捕获任何内容,因此编译器基本上会缓存它们。你可以通过这个简短的程序看到这个:

using System;

class Program
{
    static void Main()
    {
        Action action1 = GetAction();
        Action action2 = GetAction();
        Console.WriteLine(ReferenceEquals(action1, action2)); // True
    }

    private static Action GetAction()
    {
        return () => {};
    }
}

类中有自动生成的静态字段,这些字段是延迟填充的。基本上,这是一个优化,以避免创建许多委托对象,所有委托对象都引用相同的静态方法,没有上下文来区分它们。

是的,这意味着代表们自己不会收集垃圾 - 但是他们非常轻量级(他们没有阻止任何 else 被垃圾收集,因为他们没有捕获任何变量)。

作为代理无法缓存(因此有资格进行垃圾回收)的情况示例,请将InitRefs方法更改为:

private static void InitRefs(int x)
{
    _refObj = new WeakReference(new object());
    _refAction = new WeakReference((Action) (() => x.ToString() ));
    _refEvent = new WeakReference(new EventHandler((sender, eventArgs) => 
                                                         x.ToString()));
}

然后打印False三次,因为代理捕获x参数,因此无法缓存。