.NET lambda 是否会阻止对其中使用的外部引用进行垃圾回收?
.NET Do lambdas prevent garbage collection of external references used in them?
这是一个例子:
var task = Task.Run();
var func = () => task.Result;
因此,如果我松开任务引用并保留函数引用,GC 是否会收集任务并使函数抛出空引用异常?
task
显然仍然被您的 lambda 引用。所以GC不会碰它。
但请确保您的 lambda 也在某处被引用:-)否则,整棵树将被垃圾回收。
没有。匿名函数捕获变量,将其生命周期至少延长到委托或表达式树被垃圾回收时。
来自 C# 5 规范,第 7.15.5.1 节:
When an outer variable is referenced by an anonymous function, the outer variable is said to have been captured by the anonymous function. Ordinarily, the lifetime of a local variable is limited to execution of the block or statement with which it is associated (§5.1.7). However, the lifetime of a captured outer variable is extended at least until the delegate or expression tree created from the anonymous function becomes eligible for garbage collection.
请注意,捕获的是 变量,而不是当时变量的值。因此,如果您将代码更改为:
var task = Task.Run(...);
var func = () => task.Result;
task = null;
...那么原来的Task
可以被垃圾回收,如果你调用delegate会抛出异常,因为它会使用 当前 task
变量的值。
So if I loose the task reference and keep the func reference is GC going to collect the task and make the func throw null reference exception?
GC 从不将任何内容设置为 null,因此它不会抛出 NullReferenceException。
当您在 lambda 中捕获局部变量时,编译器会生成一个类型来保存字段中的值,并将对局部变量的引用替换为对该字段的引用。因此,只要您保持对 func
的引用,委托就会保持对持有 task
的对象的引用,因此 task
仍然被引用并且不会被收集。
这是一个例子:
var task = Task.Run();
var func = () => task.Result;
因此,如果我松开任务引用并保留函数引用,GC 是否会收集任务并使函数抛出空引用异常?
task
显然仍然被您的 lambda 引用。所以GC不会碰它。
但请确保您的 lambda 也在某处被引用:-)否则,整棵树将被垃圾回收。
没有。匿名函数捕获变量,将其生命周期至少延长到委托或表达式树被垃圾回收时。
来自 C# 5 规范,第 7.15.5.1 节:
When an outer variable is referenced by an anonymous function, the outer variable is said to have been captured by the anonymous function. Ordinarily, the lifetime of a local variable is limited to execution of the block or statement with which it is associated (§5.1.7). However, the lifetime of a captured outer variable is extended at least until the delegate or expression tree created from the anonymous function becomes eligible for garbage collection.
请注意,捕获的是 变量,而不是当时变量的值。因此,如果您将代码更改为:
var task = Task.Run(...);
var func = () => task.Result;
task = null;
...那么原来的Task
可以被垃圾回收,如果你调用delegate会抛出异常,因为它会使用 当前 task
变量的值。
So if I loose the task reference and keep the func reference is GC going to collect the task and make the func throw null reference exception?
GC 从不将任何内容设置为 null,因此它不会抛出 NullReferenceException。
当您在 lambda 中捕获局部变量时,编译器会生成一个类型来保存字段中的值,并将对局部变量的引用替换为对该字段的引用。因此,只要您保持对 func
的引用,委托就会保持对持有 task
的对象的引用,因此 task
仍然被引用并且不会被收集。