Understanding C# Garbage Collector Generations

The .NET Garbage Collector (GC) automatically manages memory allocation and release through an efficient, generation-based algorithm. By categorizing objects into three distinct generations—Generation 0, Generation 1, and Generation 2—the runtime optimizes performance based on the principle that newly created objects tend to have short lifespans, while older objects tend to remain in use much longer. This article explores how each generation functions, how objects are promoted, and why this design minimizes performance overhead in C# applications.

The Generational Hypothesis

The garbage collector operates on the "generational hypothesis," which relies on two primary assumptions:

  1. Most objects die shortly after creation (e.g., local variables, loop iterations).
  2. Older objects that have survived multiple collections are likely to persist for a long time (e.g., application-level caches, static references).

Instead of scanning the entire managed heap every time memory runs low, the GC focuses on smaller partitions of the heap. This makes garbage collection faster and significantly reduces application pauses.


Generation 0 (Gen 0): Short-Lived Objects

Generation 0 is the entry point for almost all newly allocated objects in C#.

  • What it contains: Temporary objects such as local variables, function arguments, and transient objects created inside loops or short methods.
  • Collection Frequency: Gen 0 is collected the most frequently. Because the allocated space for Gen 0 is relatively small, collections are rapid and typically complete in fractions of a millisecond.
  • Promotion: Objects that are still referenced and survive a Gen 0 collection are promoted to Generation 1.

Generation 1 (Gen 1): The Buffer Zone

Generation 1 serves as a transitional buffer between short-lived objects in Gen 0 and long-lived objects in Gen 2.

  • What it contains: Objects that survived at least one collection cycle but may not necessarily be permanent.
  • Collection Frequency: Collected less frequently than Gen 0. A Gen 1 collection automatically triggers a collection of Gen 0 as well.
  • Promotion: Objects that are still active after a Gen 1 collection are promoted to Generation 2.

Generation 2 (Gen 2): Long-Lived Objects

Generation 2 contains the longest-surviving objects in the application lifecycle.

  • What it contains: Static data, application-wide singletons, thread pools, and long-lived cache entries. Additionally, the Large Object Heap (LOH)—which stores objects larger than 85,000 bytes—is collected alongside Gen 2.
  • Collection Frequency: Collected the least frequently. A Gen 2 collection involves inspecting the entire heap (Gen 0, Gen 1, and Gen 2), which is why it is often referred to as a "Full GC."
  • Performance Impact: Full collections require the most CPU resources and can cause noticeable pause times in high-throughput applications if triggered frequently.

How the Collection Process Works

When memory thresholds for a generation are exceeded:

  1. Trigger: An allocation request exceeds the capacity of Gen 0, or the system experiences high memory pressure.
  2. Mark Phase: The GC identifies all live (reachable) objects by traversing root references (such as CPU registers, local variables, and static fields).
  3. Sweep and Compact Phase: Dead objects are reclaimed. Live objects are compacted (relocated to form contiguous blocks of free memory) to prevent fragmentation.
  4. Promotion: Surviving objects are moved to the next generation (Gen 0 to Gen 1, Gen 1 to Gen 2). Objects already in Gen 2 remain in Gen 2.

Summary

  • Gen 0: Newly allocated, short-lived objects; collected most frequently.
  • Gen 1: Buffer layer for objects surviving Gen 0; collected moderately.
  • Gen 2: Long-lived objects and large objects (LOH); collected least frequently via Full GC.