2

我有一个关于如何在 linq 查询中处理垃圾收集的问题。假设给了我一个要处理的请求列表。每个请求都会生成一组非常大的数据,但随后会应用一个过滤器来仅保留来自每个请求负载的关键数据。

//Input data
List<request> requests;
IEnumerable<filteredData> results = requests.Select(request => Process(request)).Select(data => Filter(data));

所以我知道查询被推迟到每个数据项,直到每个过滤的数据项被请求,所以这很好。但是那个中等记忆强度的部分会一直持续到可枚举完成吗?

我希望发生的是每个数据元素一旦通过过滤阶段就可以被垃圾收集,从而确保我有足够的内存来处理整个列表。是这种情况,还是中间可枚举保留所有内容直到整个查询结束?如果是这样,有没有一种 linq 方法来处理这个问题?


注意: Process() 函数会生成内存密集型数据……这就是我担心的

4

3 回答 3

4

只要返回值Process(...)Filter(...)不包含对内部使用的“大数据项”的任何引用,那么在该进程中使用的内存应该成为无根的,并且在处理每个元素后成为 GC 的候选者。

这并不意味着它会被收集,只是它会成为候选人。如果内存压力变高,GC 很可能会收集它。

于 2010-07-01T18:42:12.770 回答
4

垃圾收集器在 .NET 中非常激进,并且可以在不再引用中间对象时清理它们,即使在循环内部也是如此。事实上,在某些情况下,它会清理一个仍然被引用的对象,如果它可以看到它永远不会被再次访问。

运行此代码表明对象被清理得非常快,并且在查询完成之前不会挂起(它永远不会这样做):

public class MyClass1 { ~MyClass1() { Console.WriteLine("Cleaned up MyClass1"); } }
public class MyClass2 { ~MyClass2() { Console.WriteLine("Cleaned up MyClass2"); } }

public class Program
{
    static IEnumerable<MyClass1> lotsOfObjects()
    {
        while (true)
            yield return new MyClass1();
    }

    static void Main()
    {
        var query = lotsOfObjects().Select(x => foo(x));
        foreach (MyClass2 x in query)
            query.ToString();
    }

    static MyClass2 foo(MyClass1 x)
    {
        return new MyClass2();
    }
}

结果:

清理 MyClass1
清理 MyClass1
清理 MyClass1
清理 MyClass2
清理 MyClass2
清理 MyClass1
清理 MyClass2
ETC...
于 2010-07-01T18:47:42.057 回答
1

很难回答您的问题,因为您发布的内容实际上不会编译 (Select产生 a IEnumerable<T>,但您将其分配给 a List<T>。假设Filter(data)函数返回 a filteredData,您必须调用ToList()查询将其存储在results)。

requests是,我假设,已经填充了数据。此列表将遵循正常的垃圾收集规则。我假设你担心的是Process函数的结果。我不能具体说会发生什么,因为我也不知道你的Filter函数是做什么的。除非Filter函数的结果保持对其参数的引用(换句话说,Process函数的结果),否则由创建的对象Process将在查询完成后完全超出范围,并将遵循正常的垃圾收集规则.

请记住,这些规则适用于收集资格。在您的应用程序的生命周期内,不能保证收集任何对象。但是,结果将是合格的,因此 GC 将能够收集它们。

于 2010-07-01T18:44:42.283 回答