Pav*_*syn 5 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;
}
Run Code Online (Sandbox Code Playgroud)
输出为"False True True".
我已经使用SOS.dll试图找到使代表们保持GCed的内容,这是我获得的Action:
!gcroot 02472584
HandleTable:
006613ec (pinned handle)
-> 03473390 System.Object[]
-> 02472584 System.Action
Run Code Online (Sandbox Code Playgroud)
有人可以解释发生了什么吗?
您的代理不会捕获任何内容,因此编译器基本上会对它们进行缓存.你可以通过这个简短的程序看到这个:
using System;
class Program
{
static void Main()
{
Action action1 = GetAction();
Action action2 = GetAction();
Console.WriteLine(ReferenceEquals(action1, action2)); // True
}
private static Action GetAction()
{
return () => {};
}
}
Run Code Online (Sandbox Code Playgroud)
类中有自动生成的静态字段,这些字段是人工填充的.基本上,这是一个优化,以避免创建许多委托对象,这些委托对象都引用相同的静态方法,没有上下文来区分它们.
是的,这意味着代表们自己不会收集垃圾 - 但是他们非常轻量级(他们不会因为他们没有捕获任何变量而停止收集任何其他垃圾).
作为无法缓存委托(因此有资格进行垃圾收集)的情况的示例,请将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()));
}
Run Code Online (Sandbox Code Playgroud)
然后,当委托捕获x参数时,将打印False三次,因此无法缓存.