Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.NET Do lambdas prevent garbage collection of external references used in them?

Here is an example:

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

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?

like image 950
f0rt Avatar asked Jul 30 '15 16:07

f0rt


3 Answers

No. An anonymous function captures the variable, extending its lifetime to at least when the delegate or expression tree is garbage collected.

From the C# 5 spec, section 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.

Note that it's the variable which is captured though, not the value of the variable at the time. So if you change the code to:

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

... then the original Task can be garbage collected, and the delegate will throw an exception if you call it, because it will use the current value of the task variable.

like image 188
Jon Skeet Avatar answered Nov 03 '22 00:11

Jon Skeet


task is obviously still referenced - by your lambda. So the GC will not touch it.

But make sure that your lambda is also referenced somewhere :-) Otherwise, the whole tree will be garbage collected.

like image 25
Daniel Hilgarth Avatar answered Nov 03 '22 01:11

Daniel Hilgarth


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?

The GC never sets anything to null, so it won't throw a NullReferenceException.

When you capture a local variable in a lambda, the compiler generates a type to hold the value in a field, and replaces references to the local variable with references to the field. So, as long as you keep a reference to func, the delegate keeps a reference to the object that holds task, so task is still referenced and won't be collected.

like image 30
Thomas Levesque Avatar answered Nov 02 '22 23:11

Thomas Levesque