Roadmap › .NET Internals — CLR, CTS, CLS, IL, JIT, Memory Management & Garbage Collection › Module: Memory Management
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 methodsSo 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 objectsWhere things live:
| Thing | Location |
|---|---|
Local int / struct variable | Stack (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 elements | Inside the array object on the heap |
| Static fields | Special heap area associated with the type (live as long as the AssemblyLoadContext) |
Locals captured by a lambda or used across await | Moved into a heap object (closure class / async state machine) |