nan*_*nan 8 c# linq memory-management
因为我对linq很新,所以我想在下面的例子中询问我的理解是否正确.
让我们假设我有非常大的动物名称集(100k记录),我想提交它们并以非常耗时的方法处理过滤的项目(2周).方法RunWithLinq()和RunWithoutLinq()做法完全一样.
这是真的,使用第一种方法原始(大)集合将在离开方法后留在内存中,并且不会被触及GC,而使用无linq方法,集合将被删除GC?
我会感激一点解释.
class AnimalProcessor
{
private IEnumerable<string> animalsToProcess;
internal AnimalProcessor(IEnumerable<string> animalsToProcess)
{
this.animalsToProcess = animalsToProcess;
}
internal void Start()
{
//do sth for 2 weeks with the collection
}
}
class Program
{
static void RunWithLinq()
{
var animals = new string[] { "cow", "rabbit", "newt", "ram" };
var filtered = from animal in animals
where animal.StartsWith("ra")
select animal;
AnimalProcessor ap = new AnimalProcessor(filtered);
ap.Start();
}
static void RunWithoutLinq()
{
var animals = new string[] { "cow", "rabbit", "newt", "ram" };
var filtered = new List<string>();
foreach (string animal in animals)
if(animal.StartsWith("ra")) filtered.Add(animal);
AnimalProcessor ap = new AnimalProcessor(filtered);
ap.Start();
}
}
Run Code Online (Sandbox Code Playgroud)
那么,animals在每种方法结束时都有资格收集,所以严格来说你的陈述是假的.animals在非LINQ情况下很快就有资格收集,所以你的陈述的要点是正确的.
确实,每种内存的使用都不同.但是,这里有一个暗示,LINQ在内存使用方面通常更差,而实际上它通常允许比采用其他类型的方法更好的内存使用(尽管有非LINQ方式做同样的事情LINQ方式,当我使用.NET2.0时,我非常喜欢这个特定问题的基本方法.
让我们先考虑两种方法,非LINQ:
var animals = new string[] { "cow", "rabbit", "newt", "ram" };
var filtered = new List<string>();
foreach (string animal in animals)
//at this point we have both animals and filtered in memory, filtered is growing.
if(animal.StartsWith("ra")) filtered.Add(animal);
//at this point animals is no longer used. While still "in scope" to the source
//code, it will be available to collection in the produced code.
AnimalProcessor ap = new AnimalProcessor(filtered);
//at this point we have filtered and ap in memory.
ap.Start();
//at this point ap and filtered become eligible for collection.
Run Code Online (Sandbox Code Playgroud)
值得注意的是两件事.一个"合格"的收集并不意味着收集将在那一刻发生,只是它可以在未来的任何时候.二,如果对象仍然在范围内,如果它不再被使用(甚至在某些情况下使用它,但这是另一个细节级别),则可能发生收集.范围规则与程序源有关,并且是程序编写时可能发生的事情(程序员可以添加使用该对象的代码),GC集合资格规则与编译程序相关,并且是在程序编写(程序员可以添加这样的代码,但他们没有).
现在让我们看一下LINQ案例:
var animals = new string[] { "cow", "rabbit", "newt", "ram" };
var filtered = from animal in animals
where animal.StartsWith("ra")
select animal;
// at this pint we have both animals and filtered in memory.
// filtered defined as a class that acts upon animals.
AnimalProcessor ap = new AnimalProcessor(filtered);
// at this point we have ap, filtered and animals in memory.
ap.Start();
// at this point ap, filtered and animals become eligible for collection.
Run Code Online (Sandbox Code Playgroud)
所以在这种情况下,直到最后才能收集任何相关对象.
但是,请注意,这filtered绝不是一个大对象.在第一种情况下filtered,列表包含0到n个对象范围内的某个位置,其中n是大小animals.在第二种情况下,filtered是一个可以根据animals需要工作的对象,它本身具有基本上恒定的记忆.
因此,非LINQ版本的峰值内存使用率更高,因为animals仍然存在一个点并且filtered包含所有相关对象.随着animals程序更改的大小增加,实际上非LINQ版本最有可能首先出现严重的内存不足,因为在非LINQ情况下峰值内存使用状态更糟.
另一件需要考虑的事情是,在现实世界的情况下,我们有足够的项目来担心内存消耗,这就像我们的源不会是一个列表.考虑:
IEnumerable<string> getAnimals(TextReader rdr)
{
using(rdr)
for(string line = rdr.ReadLine(); line != null; line = rdr.ReadLine())
yield return line;
}
Run Code Online (Sandbox Code Playgroud)
此代码读取文本文件并一次返回每行.如果每一行都有一个动物的名字,我们可以使用它而不是var animals我们的来源filtered.
在这种情况下,尽管LINQ版本的内存使用非常少(一次只需要一个动物名称在内存中),而非LINQ版本的内存使用量要大得多(将每个动物名称加载到"ra"中)进一步行动前的记忆).LINQ版本也将在最多几毫秒后开始处理,而非LINQ版本必须首先加载所有内容,然后才能完成一项工作.
因此,LINQ版本可以愉快地处理数十亿字节的数据,而不需要使用更多的内存来处理少量数据,而非LINQ版本则会遇到内存问题.
最后,重要的是要注意,这与LINQ本身没有任何关系,因为您使用LINQ的方法与没有LINQ的方法之间存在差异.要使LINQ等效于非LINQ使用:
var filtered = (from animal in animals
where animal.StartsWith("ra")
select animal).ToList();
Run Code Online (Sandbox Code Playgroud)
使非LINQ等效于LINQ使用
var filtered = FilterAnimals(animals);
Run Code Online (Sandbox Code Playgroud)
您还可以在其中定义:
private static IEnumerable<string> FilterAnimals(IEnumerable<string> animals)
{
foreach(string animal in animals)
if(animal.StartsWith("ra"))
yield return animal;
}
Run Code Online (Sandbox Code Playgroud)
它使用.NET 2.0技术,但即使使用.NET 1.1(尽管有更多代码),您也可以创建从中派生的对象 IEnumerable
| 归档时间: |
|
| 查看次数: |
3752 次 |
| 最近记录: |