【问题标题】:C# Does Lambda => generate garbage?C# Lambda => 会产生垃圾吗?
【发布时间】:2011-08-20 16:15:15
【问题描述】:

与正常的 foreach 循环相反,使用 lambda 表达式会为 GC 生成垃圾吗?

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

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

CLR 分析器显示我有 69.9% 的 system.Action 并且我怀疑它是上述 foreach 循环的 Lamba 版本。这是真的吗?

编辑:我使用了 Microsoft CLR 分析器:http://download.microsoft.com/download/4/4/2/442d67c7-a1c1-4884-9715-803a7b485b82/clr%20profiler.exehttp://msdn.microsoft.com/en-us/library/ff650691.aspx

【问题讨论】:

  • 您能否也发布您的 foo 和 foos 的类型定义?
  • 真的希望你选择一个更好的例子,因为ForEach 绝对没有任何用处。
  • @Shiraz:使用我惊人的推理能力,FoosList<Foo>Foo 是你喜欢的任何类。为了这个问题没关系。
  • 是的,我会说一个列表,因为只有通用列表具有 .ForEach() 方法(无论如何我都知道)。
  • Foo 的定义......嗯......它只是一个类(或结构),它包含各种变量,如字符串和纹理等。然而,那些 foo 类在调用它们的 Update() 时不会自己生成垃圾。他们使用对象池等。

标签: c# linq garbage


【解决方案1】:

是的,如果闭包从本地范围捕获变量(即在此上下文中为 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 对象(闭包),而且还有用于保存捕获变量的对象(转义变量记录)。

【讨论】:

  • @Chris:你自己说过:它生成了一个最终必须被 GC 处理的类。
  • 更正,它正在生成一个类型,并且类型永远不会被 GC'd。没有理由假设它正在创建一个类实例——在这种情况下,生成的方法可以(并且应该)是静态的。
  • @Chris:不完全正确。因为生成的方法必须引用gameTime,所以它不能是静态的。要对此进行测试,请尝试创建一个使用静态方法而不是委托的 OP 代码版本。这里肯定需要关闭。假设程序集中没有其他代表共享相同的闭包签名,可能会为代表创建一个新的Type,但这不是 GC 问题。 GC 问题是,当创建 Action 时,会有一个额外的指针,否则不会有。它在那里,但它是微不足道的。
  • @Chris,Stripling 是正确的。您混淆了两个独立的概念。闭包都需要编译器生成一个隐藏类型一个要创建的该类型的实例。显然,您是正确的,该类型只需 defined 一次。但它必须在每次执行封闭上下文时实例化
  • @Chris:如果您的列表中有 1000 个元素,则调用 Foos.ForEach(x =&gt; x) 将创建 1 个委托,而不是 1000 个。但是,如果您的程序主要执行 .ForEach(),那么委托将开始很多垃圾。
【解决方案2】:

在这种情况下,我认为您使用的是泛型方法 (ForEach),它将生成一个新类型(假设 Foo 是一个引用类型,只会生成一个新类型),并且 lambda 将被编译为常规匿名方法。这没有任何迹象表明内存使用量会出现任何线性增长。

就分析器而言,您没有测量任何有关内存或 GC 的内容。您正在测量执行该方法所花费的时间,并且 lambda 不应该比“常规”方式慢很多。

【讨论】:

  • 你怎么知道用户是在分析 CPU 而不是内存?
  • 因为内存分析器不会逐个函数地显示 %。它按类型显示内存使用情况。
  • OP 说 % of Action - 这似乎是一个内存分析器测量这种类型占用多少实例/字节......并且在 MS 网站上它说它测量的 CLR 分析器“分配配置文件” - 请参阅microsoft.com/download/en/… - 从我的 POV 来看,这意味着它是一个内存分析器......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-31
  • 2015-05-24
  • 1970-01-01
  • 1970-01-01
  • 2023-04-04
相关资源
最近更新 更多