The stack, the heap and boxing
Where your data actually lives while a program runs — and the hidden cost of boxing.
11 min read
You will learn
- Describe what the stack and the managed heap are used for
- Know where value types live in less obvious cases
- Explain boxing and unboxing
- Avoid unnecessary allocations in hot code
Two regions of memory
When your program runs, the CLR manages memory in two main places:
| Stack | Managed heap | |
|---|---|---|
| Holds | Method calls: parameters, local variables, return addresses | Objects created with new (class instances, arrays, strings, boxes) |
| Allocation | Move a pointer — practically free | Fast, but the garbage collector must clean up later |
| Freed | Instantly, when the method returns | When the garbage collector finds the object unreachable |
| Size | Small (about 1 MB per thread) | As large as available memory |
| Organized | Last in, first out — one frame per method call | Generations managed by the GC |
Each thread has its own stack. Every method call pushes a frame holding that call's parameters and locals; returning pops it. This is the "call stack" you see in stack traces.
Watch it happen
Step through this program and notice what goes where — and what happens when a frame is popped:
Stack & heap
References: two variables, one object
Code
1int age = 36;2var ada = new Person("Ada");3var alias = ada;4alias.Name = "Grace";5Rename(ada);6ada = null;7alias = null;89void Rename(Person p)10{11 p.Name = "Linus";12}
Stack
Main()
- intage36
Heap
(empty)
age is an int — a value type. The number 36 is stored directly inside the method's stack frame."Value types live on the stack" — mostly
The popular rule of thumb is a simplification. More precisely, a value is stored wherever its container is:
void Example()
{
int local = 5; // local value type → in Example's stack frame
var order = new Order(); // the reference is on the stack, the Order object on the heap
}
class Order
{
public int Quantity; // a value-type FIELD → inside the Order object, on the heap
public decimal Total;
}
int[] numbers = new int[1000]; // the array (and all 1000 ints) live on the heapCaptured variables in lambdas, locals in async methods and iterator methods also move to the heap, because they must outlive the stack frame.
Boxing: when a value type pretends to be an object
Sometimes a value type must be treated as an object or an interface. The runtime then boxes it: it allocates a small object on the heap and copies the value inside.
int number = 42;
object boxed = number; // boxing: heap allocation + copy
int back = (int)boxed; // unboxing: type check + copy back
number = 100;
Console.WriteLine(boxed); // 42 — the box holds its own copyBoxing is invisible in the code but costs an allocation and later GC work. It happens when you:
- assign a value type to
objector to an interface it implements (IComparable c = 5;), - pass a value type to a parameter of type
object(old APIs likeArrayList,string.Formatwith many args), - call an interface method on a struct through an interface reference.
Generics avoid boxing
List<int> stores real ints in an int[]. The old non-generic ArrayList stored everything as object, boxing every number. That's one of the main reasons generics were added to .NET.
Try it
C# runtime not loaded
Does this matter day to day?
For most application code, no — the JIT and GC are extremely good, and clarity matters more. It matters in hot paths: tight loops, high-throughput servers, serializers, game loops. There, reducing allocations (avoiding boxing, reusing buffers, using Span<T> — coming up later in this track) directly reduces garbage collection pauses.
Key takeaways
- The stack holds method frames and is freed instantly on return; the heap holds objects and is cleaned by the GC.
- A value is stored where its container is: locals on the stack, fields inside their object on the heap.
- Boxing copies a value type into a heap object when it's treated as
objector an interface. - Generics avoid boxing; worry about allocations mainly in hot code.
Quiz
4 questions · 3 correct to pass1.What happens to a method's local variables when the method returns?
2.A class has an `int Count` field. Where does that int live?
3.What is boxing?
4.Which line causes boxing?
To complete this lesson
- ○ Pass the quiz (3/4 correct)