2

Assuming a function that takes a list of objects:

void WriteData(List<LargeObject> objectsToWrite);

If we are calling this function, for readability or debugging we might consider making this a local variable:

var objectsToWrite = SomeMethodThatPreparesTheObjects();
WriteData(objectsToWrite);

However, we might also inline the variable such that the functional call becomes:

WriteData(SomeMethodThatPreparesTheObjects());

The two are functionally equivalent - but my question is: Do both methods keep the list of objects around until the end of the method execution (because there is a local variable which would be a GC root) - or does it depend on compiler internals as to whether the latter call ends up being translated into a local variable?

Matt Whitfield
  • 6,436
  • 3
  • 29
  • 44
  • list (or any object) will be stored on heap. its not relevant to how you write the code, whether you use field, variable or inlined function. – M.kazem Akhgary Jun 26 '17 at 19:39
  • Local variable does not prevent object from being collected. When there are no more usages of that variable - object can be collected, even before method completes. So, none of the methods you use keep objects around until end of method execution. – Evk Jun 26 '17 at 19:44
  • 1
    Note that there are situations in which you *want* an object to be kept alive; as the comment above correctly notes, the optimizer is permitted to kill a local early. If you must keep a local alive, use the `KeepAlive` function. See https://stackoverflow.com/a/15388788/88656 for more details. – Eric Lippert Jun 26 '17 at 23:18
  • Also note that there is a third option: `WriteData(objectsToWrite: SomeMethodThatPreparesTheObjects());` This generates the same IL as `WriteData(SomeMethodThatPreparesTheObjects());`. – Brian Jun 27 '17 at 12:33
  • @EricLippert - thanks for the link - that was incredibly helpful – Matt Whitfield Jul 04 '17 at 14:51

2 Answers2

3

First, object can be garbage collected even if there is a reference to it from local variable in currently running method, after the last usage of that variable. However, if you build without optimizations ("Optimize code" option in Visual Studio or similar option in csc.exe) - local variables will be rooted for the lifetime of the method, so that you have no troubles while debugging code. So in this case there is a difference between two methods from your question.

When compiling with optimizations enabled - local variable will not prevent objectsToWrite from being collected right after return from WriteData, so there will be no difference in this regard. In this particular case compiler will most likely eliminate local variable at all, so compiled code will be identical for both cases (again, when compiling with optimizations).

You can test that object referenced by local variable can be collected before method ends with this simple code:

static void Main(string[] args) {
    var ob = new object();
    Write(ob);
    var wr = new WeakReference(ob);
    GC.Collect(2, GCCollectionMode.Forced);            
    Console.WriteLine(wr.IsAlive);
    Console.ReadKey();            
}

static void Write(object ob) {
    Console.WriteLine(ob);
}

Compile with optimizations, run without debugger and you will see that weak reference is dead, which means object referenced by this weak reference was collected, even though method Main is still running and object is referenced by local variable ob.

Evk
  • 98,527
  • 8
  • 141
  • 191
1

You likely won't be able to tell the difference. When .NET feels memory pressure, it collects, walking objects looking for roots, etc.

If it happens in the middle of executing WriteData(), the large object won't be collected because it is in use.

If you use a local variable the compiler will detect it isn't used anymore after calling WriteData() so it won't be "rooted" - the variable essentially "goes out of scope." I guess there is a small chance a collection could happen between the time you set your local variable and the call to WriteData(), but in that case you wouldn't want it collected - you're about to use it.

In any case, "trust the force" - the right thing will happen!

n8wrl
  • 19,439
  • 4
  • 63
  • 103
  • Thanks - Do you happen to have a link to anything that describes the behaviour of removing variables from scope after the last reference within a method? – Matt Whitfield Jun 26 '17 at 20:10
  • @MattWhitfield I did some googling and found dozens of articles on scope in general, but I think you're interested in compiler optimization too, because in this case both things are at work - C# does analysis on your code to figure out that your variable is no longer needed and can thus be dropped, so to speak. That has GC ramifications but is kind of a side-effect of the optimization. – n8wrl Jun 26 '17 at 21:14