Why Value-Types Are Stored Onto Stacks

Why value-types are stored onto Stacks?

Eric Lippert discusses this here; firstly, it is incorrect that "value types are stored on the stack". They sometimes are, but not as:

  • fields on a class
  • captured variables
  • variables in an iterator block

When they can be stored on the stack it is a convenient way of modelling their lifetime, but it isn't required to store them on the stack. You could write a compiler+CLI that doesn't have a stack, for example.

When are value types stored in stack(C#)?

Well, firstly it is very rare that you would need to know, but basically, value-types are stored where-ever they are owned.

They are stored on the stack when they are part of the execution flow of a thread, which can mean:

  • in a "local" (a method variable) - excluding some cases (below)
  • as a floating value in part of a method, i.e. the return value from one method that is about to be passed as a value to another method - no "local" is involved, but the value is still on the stack
    • value-type parameters that are passed by-value (i.e. without ref or out) are simply a special-case of this
  • in an instance "field" (a type variable) on another value-type that is itself on the stack (for the above reasons)

They are stored on the heap (as part of an object) when:

  • in an instance "field" on a class
  • in an instance "field" on a value-type that is itself on the heap
  • in a static "field"
  • in an array
  • in a "local" (a method variable) that is part of an iterator block, an async method, or which is a "captured" variable in a lambda or anonymous method (all of which cause the local to be hoisted onto a field on a class that is generated by the compiler)
  • when "boxed" - i.e. cast into a reference-type (object, dynamic, Enum, ValueType (yes: ValueType is a reference-type; fun, eh?), ISomeInterface, etc)

Why are Value Types created on the Stack and Reference Types created on the Heap?

My question is Why.

Why do they "explain" that? Because sometimes the authors don't know any better themselves, and sometimes they're too lazy to explain it properly. The truth it rather more complicated.

Fortunately, Eric Lippert has written extensively around this:

  • The stack is an implementation detail, part 1
  • The stack is an implementation detail, part 2
  • The truth about value types

While I would usually try to at least include a paraphrase of the full answer here, I'm not going to do so here - because explaining it very briefly is almost bound to give a misleading result. Just go and read those blog posts - and then everything else Eric has written. It'll take weeks, but you'll be glad you did.

Where are value types defined in a reference type stored (heap or stack)?

Memory in .NET - what goes where by Jon Skeet

Is an instance of System.ValueType on heap or stack?

System.ValueType is not a value-type. Value-types are things that inherit from ValueType, but not ValueType itself. So:

System.ValueType foo = 5; 

here, 5 is loaded on the stack. This is then boxed (with a box of type int) onto the heap. The object reference is then assigned to the reference foo. We can see this by looking at the IL for that:

ldc.i4.5 // push int32 value 5 onto the stack
box int32 // box the value at the top of the stack, type int32
stloc.0 // assign to "foo"

In general, though: structs can be either on the heap or on the stack, depending on the context. Frankly, it doesn't matter which - because both are implementation details. It is the behaviour that matters... and (at least in their unboxed form), the key point about structs is their copy-on-assign semantic.

Reference types live on the heap, value types live on the stack

https://learn.microsoft.com/en-us/archive/blogs/ericlippert/the-stack-is-an-implementation-detail-part-one

The whole "reference types on the heap, value types on the stack" is not only a bad way to look at it, but it's wrong too.

Why are reference types stored in heap

You can't generally store reference types on stack because the stack frame is destroyed upon method return. If you saved a reference to an object so it can be dereferenced after the method completes, you'd be dereferencing a non-existent stack location.

The HotSpot JVM can perform escape analysis and, if it determines that an object cannot possibly escape the method scope, it will in fact allocate it on the stack.

Value types in object stored in heap as well?

They are stored in the heap, inside of the memory allocated for the reference type. In addition, value types are often stored in places other than "the stack". However, the CLI spec does not specify where the memory pool that stores value types resides - it's an implementation detail that should not matter.

Where does a value type-variable - which is returned by ref - live? Stack or heap?

I feel like you understand yourself already why it does not work. You cannot return local variable by reference from method (unless it's ref local), because in most cases lifetime of local variable is the method, so its reference outside of method does not have any meaning (outside of method this variable is dead and location where it were before might contain anything). As documentation states:

The return value must have a lifetime that extends beyond the
execution of the method. In other words, it cannot be a local variable
in the method that returns it

In practice some local variables might live longer than execution of method they are declared in. For example, variables captured by closure:

int myLocal = 5;
SomeMethodWhichAcceptsDelegate(() => DoStuff(myLocal));
return ref myLocal;

However, this introduces additional complications without any benefits, so this is also forbidden, even though lifetime of myLocal might be much longer than containing method.

It's better to not think about it in terms of stack and heap. For example you might think that you cannot return reference to something allocated on stack from the method via ref return. That's not true, for example:

private void Test() {
int myLocal = 4;
GetX(ref myLocal);
}

private ref int GetX(ref int i) {
return ref i;
}

Here myLocal is clearly on stack, and we pass it by reference to GetX and then return this (stack allocated) variable with return ref.

So just think about it in terms of variable lifetimes and not stack\heap.

In your second example, lifetime of _myInt field is clearly longer than execution of GetX, so there is no problem to return it by reference.

Note also that whether you return value type or reference type with return ref doesn't make any difference in context of this question.



Related Topics



Leave a reply



Submit