From long time back I wanted to know where exactly a variable (be it value type or reference type) will get stored. Will it be on stack or heap?
I have read Eric Lip
Thinking of storage being divided by stack and heap is a convenient abstraction that will serve you well. But it is much more convoluted, there are 6 distinct storage locations for variables in a .NET program.
The tool of choice here is the debugger, it can show you exactly where variables are getting stored. That does require insight into how machine code works. Use Debug + Windows + Disassembly to see the machine code. It is also important that you look at the Release build of your program and change a setting that allows the code to be optimized even when you debug it. Tools + Options, Debugging, General, untick the "Suppress JIT optimization on module load" option. You'll now see the machine code the way it will execute on your user's machine.
Things you have to know up front to make sense of it all:
Objects of a reference type are stored on the GC heap. The variable that stores a reference has the same kind of storage choices as value type values.
Value type values or object references have six possible storage locations:
The latter three bullets is where it gets complicated and why you need to look at machine code to find out where they are stored. It is highly implementation specific, the kind of jitter matters. And is highly affected by whether or not you've got the jitter optimizer enabled. Making the proper choices here is very important to perf. The rough outline (skipping the ARM jitter):
The first two method arguments are stored in CPU registers for the x86 jitter, including the value of this for instance methods. The x64 jitter uses 4 registers. Floating point processor registers are used to pass variables of type Single and Double on x86, XMM registers on x64
A function return value is returned in a CPU register, if it fits, using the EAX or RAX register, ST0 if it is a floating point value. If it doesn't fit then the caller reserved space on the stack frame for the value and passed a pointer to it
The jitter optimizer looks for opportunities to store local variables in CPU registers. It may spill the register back to the stack frame if it is forced to do so because it is out of registers.
There are a number of observable side effects of these implementation details: