19

While reading "C# in Depth" I was going through the section titled "Reference types live on the heap, value types live on the stack."

Now what I could understand is (mainly for ref type):

class Program { int a = 5; // stored in heap public void Add(int x, int y) // x,y stored in stack { int c = x + y; // c stored in stack } } 

Just want to clarify if my assumptions are right. Thanks.

EDIT: I should have used diff variables, as I think what I had initially created confusion. So I have modified the code.

EDIT: Yes, as Jon mentioned - it's a myth. I should have mentioned that. My apologies.

0

6 Answers 6

18

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.

Sign up to request clarification or add additional context in comments.

9 Comments

can u pls modify the code and explain- i mean an example of value type stored in heap
They're right for most existing implementations. Theres' nothing to say that nobody can't build a stackless CLR. x and y won't be on the stack in that? There's nothing to say that an optimisation isn't allowed to put a reference type on the stack and clean it up when the stack unwinds. This isn't done today, but it could be. It's good to have an idea of what is done wrt stack and heap, but only after picking value vs reference type as truly appropriate. For one thing, ppl talking about the efficiency of the stack tend to underestimate the CLR heaps efficiency.
@siride: I should point out that that section specifically calls this out as a myth :)
@Jon: Not only is it a CLR implementation issue - it's also a C# compiler implementation issue. The C# compiler doesn't say anything about how things will stored. The compiler could change without changing the CLR, using a class to store the local variables for each method, for example... and the language spec wouldn't have to change at all.
@siride: I mean that I've got a list of myths which I explicitly say are wrong, and "Reference types live on the heap, value types live on the stack" is one of these myths. The question here makes it sound like the book is asserting it, when in fact it's refuting it :)
|
16

I may be a somewhat useful abstraction to have a mental image of what's going on behind the scenes. But neither is true in any currently shipping version of the JIT compilers. Which perhaps is the crux of the issue, actual allocation location is a JIT compiler implementation detail.

There are at least six places where a value type value can live with mainstream (x86 and x64) jitters:

  • in a stack frame, put there by a local variable declaration or a method call
  • in a CPU register, a very common optimization performed by the JIT in the Release build. And used to pass arguments to a method, the first two x86, four for x64. And local variables when possible
  • on the FPU stack, used by the x86 jitter for floating point values
  • on the GC heap, when the value is part of a reference type
  • on the loader heap of the AppDomain, when the variable is declared static
  • in thread-local storage when the variable has the [ThreadStatic] attribute.

Reference type objects are commonly allocated on the GC heap. But I know of one specific exception, interned strings produced from literals in the source code are allocated in the AppDomain's loader heap. This completely behaves like an object at runtime, except that it isn't linked in to the GC heap, the collector simply cannot see it.

Addressing your code snippet:

  • yes, "a" is likely to be stored on the GG heap
  • "x" is always passed in a CPU register on x86 and x64. "y" will be in a CPU register on x64, the stack on x86.
  • "c" is likely to not exist at all, removed by the JIT compiler because the code has no effect.

2 Comments

Why first param x will be on stack and second y - not always? P.S. c will be removed in release mode
Two CPU registers for an x86 core, four for an x64 core. The "this" pointer requires one.
1

Quoting Jon Skeet from his famous blog about how and where reference and value types are stored in a .NET application:

The memory slot for a variable is stored on either the stack or the heap. It depends on the context in which it is declared:

  1. Each local variable (ie one declared in a method) is stored on the stack. That includes reference type variables - the variable itself is on the stack, but remember that the value of a reference type variable is only a reference (or null), not the object itself. Method parameters count as local variables too, but if they are declared with the ref modifier, they don't get their own slot, but share a slot with the variable used in the calling code. See my article on parameter passing for more details.
  2. Instance variables for a reference type are always on the heap. That's where the object itself "lives".
  3. Instance variables for a value type are stored in the same context as the variable that declares the value type. The memory slot for the instance effectively contains the slots for each field within the instance. That means (given the previous two points) that a struct variable declared within a method will always be on the stack, whereas a struct variable which is an instance field of a class will be on the heap.
  4. Every static variable is stored on the heap, regardless of whether it's declared within a reference type or a value type. There is only one slot in total no matter how many instances are created. (There don't need to be any instances created for that one slot to exist though.) The details of exactly which heap the variables live on are complicated, but explained in detail in an MSDN article on the subject.

Comments

0

c leaves on the stack because at least is a value type meanwhile a in the managed heap because of being reference type's field

1 Comment

Note that the value of c would be on the stack (in current implementations) even if it were of type (say) StringBuilder. It's just that the value of the variable would be a reference to an oject - it's the object that would be on the heap. I find a great many things are clearer once you distinguish between a variable, its value, and what that value actually represents (e.g. a reference rather than an actual object).
0

Storage locations (variables, fields, array elements, etc.) of reference types hold references to objects on the heap; storage locations of primitive value types hold their value within themselves; storage locations of struct types hold all of their fields, each of which may be a reference or value type, within themselves. If a class instance holds two different non-null strings, a Point, and an integer, both the X and Y coordinates of the point, as well as the stand-alone integer and references to the two strings, will be held within one heap object. Each of the strings will be held in a different heap object. The key point about the storage locations of classes versus structs is that except in the case of a class entity holding a reference to itself, every non-null reference type field within a class or struct will hold a reference to some other object, which will be on the heap.

Comments

0

Think of it in C/C++ terms.

Anytime you make a "new" something, or use malloc, that goes on the heap-- that is, the "object" goes on the heap, the pointer itself is placed on the stack within the scope of the structure (or function, which is really just another structure) that it's part of. If it's a local variable or reference type (pointer), it goes on the stack.

To put it another way, the >object< that the reference type is pointing to is on the heap, it's just the pointer itself that's on the stack. Memory leaks occur when the program pops the pointer off the stack, but the memory in the heap hasn't been freed for use-- how do you know what memory to free it if the reference to it's location has been lost? Well, C/C++ couldn't, you had to do it yourself before the reference was popped off the stack and lost forever, but that's where modern languages come in with their fancy shmancy "garbage collection heaps". It's still preferable to explicitly clean up any heap memory you allocated than implicitly by leaving it for the GC to pickup, it's "cheaper" that way (in terms of CPU resources).

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.