.NET Academy

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:

StackManaged heap
HoldsMethod calls: parameters, local variables, return addressesObjects created with new (class instances, arrays, strings, boxes)
AllocationMove a pointer — practically freeFast, but the garbage collector must clean up later
FreedInstantly, when the method returnsWhen the garbage collector finds the object unreachable
SizeSmall (about 1 MB per thread)As large as available memory
OrganizedLast in, first out — one frame per method callGenerations 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:

Interactive

Stack & heap

References: two variables, one object

1/9

Code

1int age = 36;
2var ada = new Person("Ada");
3var alias = ada;
4alias.Name = "Grace";
5Rename(ada);
6ada = null;
7alias = null;
8
9void 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 heap

Captured 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 copy

Boxing is invisible in the code but costs an allocation and later GC work. It happens when you:

  • assign a value type to object or to an interface it implements (IComparable c = 5;),
  • pass a value type to a parameter of type object (old APIs like ArrayList, string.Format with 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

Program.cs
Loading editor…

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 object or an interface.
  • Generics avoid boxing; worry about allocations mainly in hot code.

Quiz

4 questions · 3 correct to pass
  1. 1.What happens to a method's local variables when the method returns?

  2. 2.A class has an `int Count` field. Where does that int live?

  3. 3.What is boxing?

  4. 4.Which line causes boxing?

To complete this lesson

  • ○ Pass the quiz (3/4 correct)