How does garbage collection work in .NET?

.NET Framework & CLR Memory Intermediate 2–3 Yrs3–5 Yrs Conceptual 2 min read

Quick Answer

The .NET GC is a generational, mark-and-compact collector. New objects go in Gen 0; objects that survive a collection are promoted to Gen 1, then Gen 2. Most collections are small, fast Gen 0 sweeps. The GC finds objects still reachable from roots, reclaims the rest, and compacts the heap. Large objects (≥ ~85 KB) go on the Large Object Heap, collected with Gen 2.

The GC is generational. New objects are allocated in Gen 0, and because most objects are short-lived, collecting Gen 0 is quick and happens often. Anything that survives gets promoted to Gen 1 and then Gen 2, and the full Gen 2 collection is the expensive one that runs rarely. A collection works by marking — starting from roots like static fields and stack variables and walking the graph to find everything still reachable — then reclaiming the rest and compacting the survivors so the heap doesn't fragment. It's non-deterministic, so I don't call GC.Collect. The leaks you still get in managed code are things like static references, event handlers you never unsubscribe from, and caches that grow forever.

Why generations: most objects die young (locals, temporaries). Collecting only Gen 0 frequently is cheap; the expensive full Gen 2 collection runs rarely.

A collection:

  1. Mark — starting from roots (statics, locals on the stack, CPU registers, GC handles), walk the object graph and mark everything reachable.
  2. Sweep / compact — reclaim unmarked objects; move survivors together to remove fragmentation and update references.
  3. Promote survivors to the next generation.

Points interviewers probe:

  • The GC is non-deterministic — you don't control when it runs; don't call GC.Collect() in normal code.
  • Finalizers delay reclamation (two GC cycles) — implement IDisposable and suppress finalization.
  • Workstation vs Server GC, and background GC, tune pause behaviour.
  • Common leak causes in “managed” code: static references, event handlers never unsubscribed, long-lived caches, captured closures.