Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Escape analysis in the .NET CLR VM

Is there any escape analysis performed by the CLR compiler/JIT? For example, in Java it appears that a loop variable an object allocated in a loop that doesn't escape the loop gets allocated on the stack rather than the heap (see Escape analysis in Java).

To clarify, in the example below, would the compiler optimise away the heap allocation of foo as it never escapes the loop.

class Foo 
{ 
   int number;
   Foo(int number) { this.number = number; }
   public override string ToString() { return number.ToString(); }
}

for (int i = 0; i < 10000000; i++)
{
   Foo foo = new Foo(i);
   Console.WriteLine(foo.ToString());
}
like image 525
SimonC Avatar asked Nov 22 '11 05:11

SimonC


3 Answers

If you mean the object ( new Foo(i); ), then my understanding is that no: this is never allocated on the stack; however, it will die in generation zero, so will be very efficient to collect. I don't profess to know every dark and dank corner of the CLI, but I am not aware of any scenario in C# that would lead to a managed reference-type being allocated on the stack (things like stackalloc don't really count, and are highly specific). Obviously in C++ you have a few more options, but then it isn't a managed instance.

Interestingly, on MonoTouch/AOT it might be collected immediately, but that is not the main CLI VM (and is for a very specific scenario).

As for the variable - that will usually be on the stack (and re-used for each loop iteration) - but it might not be. For example, if this is an "iterator block", then all the non-removed local-variables are actually fields on the compiler-generated state-machine. More commonly, if the variable is "captured" (into an anonymous method or lambda expression, both of which form closures), then the variable is transformed into a field on the compiler-generated capture-context, and is separate per loop iteration (since foo is declared inside the loop). This means that each is separate on the heap.

As for i (the loop variable) - if that gets captured, it gets even more interesting

  • in C# 1.2 captures didn't exist, but by the spec the loop-variable is technically per-iteration
  • in C# 2.0 to 4.0, the loop variable is shared (causing the infamous capture/foreach common question)
  • in C# 5.0 and above the loop variable is per-iteration again

this only makes a difference when the variable is captured, but changes the semantics of exactly how it manifests on the capture-context

like image 173
Marc Gravell Avatar answered Nov 15 '22 14:11

Marc Gravell


A value type might be allocated on the stack (not always), but the same is not true for instances of reference types. In fact:

In particular, the storage locations of instances of reference types are always treated as though they are long-lived, even if they are provably short-lived. Therefore they always go on the heap.

(Eric Lippert: The Truth About Value Types)

Also The Stack Is An Implementation Detail makes a good reading.

like image 6
Teudimundo Avatar answered Nov 15 '22 12:11

Teudimundo


While the x86 JIT is good at 'inlining' valuetypes, your snippet will not qualify as the ToString method will be a virtual call on a boxed object. Edit: This may not be the case, as you are not overriding ToString.

The x64 JIT however does not do this at all from my experiments.

Edit:

If possible, test your code on both x86 and x64.

like image 1
leppie Avatar answered Nov 15 '22 14:11

leppie