C#中的Lambda表达式是否会产生垃圾?

25

使用Lambda表达式与普通的foreach循环相比,是否会为垃圾回收产生更多的垃圾?

// Lambda version
Foos.ForEach(f=>f.Update(gameTime));

// Normal approach:
foreach (Foo f in Foos)
{
  f.Update(gameTime);
}

CLR性能分析器显示我有69.9%的system.Action<T>,我怀疑这是上面所述的foreach循环的lambda版本。这是真的吗?
编辑:我使用了微软的CLR性能分析器:http://download.microsoft.com/download/4/4/2/442d67c7-a1c1-4884-9715-803a7b485b82/clr%20profiler.exe或者http://msdn.microsoft.com/en-us/library/ff650691.aspx

你能否也发布一下 foo 和 foos 的类型定义? - Shiraz Bhaiji
1
真希望你选择了一个更好的例子,因为 ForEach 完全没有任何有用的功能。 - Aaronaught
4
根据我的强大的推理能力,“Foos”是一个“List<Foo>”,而“Foo”是任何你喜欢的类。就问题而言,这并不重要。 - StriplingWarrior
是的,我会说一个列表,因为只有通用列表才有.ForEach()方法(至少我知道的是这样)。 - oscilatingcretin
Foo的定义...嗯...它只是一个类(或结构体),用于保存各种变量,如字符串和纹理等。但是,当调用它们的Update()方法时,这些Foo类本身不会生成垃圾。它们使用对象池等技术。 - Napoleon
显示剩余2条评论
2个回答

26

是的,如果闭包从局部作用域中捕获变量(例如在这种情况下捕获了 gameTime),那么 lambda 将创建垃圾。

例如,以下是一个 C# 函数:

static void TestLambda(List<Foo> Foos, DateTime gameTime)
{
    Foos.ForEach(f => f.Update(gameTime));
}

将被翻译为以下内容:

private static void TestLambda(List<Foo> Foos, DateTime gameTime)
{
    Program.<>c__DisplayClass1 <>c__DisplayClass = new Program.<>c__DisplayClass1();
    <>c__DisplayClass.gameTime = gameTime;
    Foos.ForEach(new Action<Foo>(<>c__DisplayClass.<TestLambda>b__0));
}

请注意,生成代码中有两个new实例,这意味着不仅分配了Action对象(闭包),还分配了用于保存捕获变量的对象(逃逸变量记录)。


1
@Chris:你自己说了:它正在生成一个最终必须被GC回收的类。 - BrokenGlass
1
@Gabe:闭包只创建一次,而不是每次操作被评估时都会创建。 - StriplingWarrior
3
@Chris:并不完全正确。因为生成的方法必须引用 gameTime,所以它不能是静态的。要测试这一点,请尝试创建一个使用静态方法而不是委托的 OP 代码版本。在此处肯定需要闭包。假设程序集中没有其他委托共享相同闭包签名,则可能会创建一个新的 Type 用于委托,但这不是垃圾回收问题。垃圾收集问题在于创建 Action 时有一个额外的指针,否则就不会有了。它确实存在,但微不足道。 - StriplingWarrior
2
@Chris,Stripling是正确的。您混淆了两个不同的概念。闭包需要编译器生成一个隐藏类型和该类型的实例。您当然是正确的,类型只需要定义一次。但它必须在每次执行封闭上下文时被实例化。 - Kirk Woll
2
@Chris:如果你的列表中有1000个元素,调用Foos.ForEach(x => x)将只创建1个委托,而不是1000个。但是,如果你的程序大多数时间都在执行.ForEach(),那么这些委托将开始变成很多垃圾。 - Gabe
显示剩余13条评论

0
在这种情况下,我认为您正在使用通用方法(ForEach),它将生成一个新类型(假设Foo是引用类型,则只会生成一个新类型),并且Lambda将编译为常规匿名方法。这些都没有表明任何内存使用的线性增长。
就分析器而言,您不测量任何与内存或GC相关的内容。您正在测量执行该方法所花费的时间,而Lambda不应比“常规”方式慢得多。

2
你怎么知道用户正在对CPU进行分析而不是内存? - Gabe
1
因为内存分析器不会按函数逐个显示百分比,而是按类型显示内存使用情况。 - Chris Shain
1
原帖中提到了Action<T>的% - 这似乎是一个内存分析器,用于测量该类型占用的实例/字节数量...而在微软网站上,CLR分析器被描述为测量"分配概要文件"的工具 - 请参见http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=14727 - 在我看来,这意味着它是一个内存分析器... - Yahia

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接