【问题标题】:.NET Do lambdas prevent garbage collection of external references used in them?.NET lambda 是否会阻止对其中使用的外部引用进行垃圾收集?
【发布时间】:2015-07-30 16:50:31
【问题描述】:

这是一个例子:

var task = Task.Run();
var func = () => task.Result;

那么如果我松开任务引用并保留 func 引用,GC 是否会收集任务并使 func 抛出空引用异常?

【问题讨论】:

  • 如果你能给出一个简短但完整的程序来实际编译,这总是有帮助的——这段代码由于各种原因无法编译。

标签: c# .net lambda garbage-collection


【解决方案1】:

没有。匿名函数捕获变量,将其生命周期至少延长到委托或表达式树被垃圾回收时。

来自 C# 5 规范,第 7.15.5.1 节:

当一个外部变量被一个匿名函数引用时,这个外部变量被称为被匿名函数捕获。通常,局部变量的生命周期仅限于执行与其关联的块或语句(第 5.1.7 节)。但是,捕获的外部变量的生命周期至少会延长,直到从匿名函数创建的委托或表达式树符合垃圾回收条件。

请注意,捕获的是 变量,而不是当时的变量值。因此,如果您将代码更改为:

var task = Task.Run(...);
var func = () => task.Result;
task = null;

...那么原来的Task可以被垃圾回收,如果你调用它,委托会抛出异常,因为它会使用当前task 变量。

【讨论】:

    【解决方案2】:

    task 显然仍被您的 lambda 引用。所以GC不会碰它。

    但请确保您的 lambda 也在某处被引用 :-) 否则,整个树将被垃圾收集。

    【讨论】:

      【解决方案3】:

      那么如果我松开任务引用并保留 func 引用,GC 是否会收集任务并使 func 抛出空引用异常?

      GC 永远不会将任何内容设置为 null,因此它不会抛出 NullReferenceException。

      当您在 lambda 中捕获局部变量时,编译器会生成一个类型来保存字段中的值,并将对局部变量的引用替换为对该字段的引用。因此,只要您保留对func 的引用,委托就会保留对持有task 的对象的引用,因此task 仍然被引用且不会被收集。

      【讨论】:

        猜你喜欢
        • 2020-01-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-05-04
        • 2011-03-05
        • 1970-01-01
        • 2014-03-18
        • 2011-08-25
        相关资源
        最近更新 更多