以下是一个演示此问题的控制台应用:
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
有人可以解释发生了什么吗?
答案 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
参数,因此无法缓存。