Lessons in this course (11)

5. Stack, heap and how value and reference types are laid out

Basic★ Must knowLesson 5 of 11

Each thread has a stack (1 MB default on Windows, 8 MB typical on Linux) holding frames: arguments, locals, return addresses, spilled registers. The managed heap is shared by the process and holds objects.

Every heap object has a header:

text
Reference variable (8 bytes on 64-bit) ──▶ [ Object header / sync block index (8 bytes) | MethodTable pointer (8 bytes) | fields … ]
                                                                                        └▶ MethodTable: type info, base type, interface map, vtable slots for methods

So an empty object costs ~24 bytes on 64-bit. A struct has no header — its fields are stored inline wherever it lives (stack frame, inside an array, inside another object).

csharp
class PointClass { public int X, Y; }      // heap object: header + MT pointer + 8 bytes of fields (+ padding) ≈ 24 bytes
struct PointStruct { public int X, Y; }    // 8 bytes, inline

var a = new PointStruct[1000];   // one array object, 8,000 bytes of data inline — cache friendly
var b = new PointClass[1000];    // one array of 1000 references + up to 1000 separate objects

Where things live:

ThingLocation
Local int / struct variableStack (or a CPU register)
Reference variable (local)Stack — the object it points to is on the heap
Object fields (including value-type fields)Inside the object on the heap
Array elementsInside the array object on the heap
Static fieldsSpecial heap area associated with the type (live as long as the AssemblyLoadContext)
Locals captured by a lambda or used across awaitMoved into a heap object (closure class / async state machine)