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());
}
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
this only makes a difference when the variable is captured, but changes the semantics of exactly how it manifests on the capture-context
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With