为什么LINQ查询后的GC释放WhereListIterator而不是表示条件的Func?

Jer*_*vel 10 .net c# linq garbage-collection

我正在研究一个简单的LINQ查询的内存影响,并注意到LINQ查询创建了2个类型Enumerable+WhereListIterator<Int32>和的额外对象Func<Int32, Boolean>.

使用的代码是这样的:

static void Main(string[] args)
{
    // Setting baseline snapshot
    var list1 = new List<int> { 4862, 6541, 7841 };
    var list2 = new List<int>(list1.Count);
    var list3 = new List<int>(list1.Count);

    // First snapshot: LINQ usage
    list2.AddRange(list1.Where(item => item > 5000 && item < 7000));

    // Second snapshot: foreach-loop
    foreach (var item in list1)
    {
        if (item > 5000 && item < 7000)
        {
            list3.Add(item);
        }
    }

    // End gather
    Console.Read();
}
Run Code Online (Sandbox Code Playgroud)

foreach循环之后的快照中,我注意到Enumerable+WhereListIterator<Int32>对象是垃圾收集但Func<Int32, Boolean>仍然在内存中.

为什么这仍然存在?在那个时间点(在Console.Read声明中)我认为没有任何东西仍在引用它,并且探测器强制GC(这就是收集迭代器的原因).

注意:收集其他快照不会改变释放的对象数量,因此不需要Func为下次运行标记为集合.

das*_*ght 11

lambda不是GC-ed的原因是lambda本身的结构:

item => item > 5000 && item < 7000
Run Code Online (Sandbox Code Playgroud)

这个lambda不捕获任何东西,这意味着它可以编译一次,并永远重用.C#发现了这一点,并通过静态缓存它们以利用lambdas来提高性能.

如果你的lambda从它的上下文中捕获了一个变量,那么当它不再需要时就会被垃圾收集.

有关.NET中lambda生存期的更多信息,请参阅此答案.