C#  

The Secret Behind .NET Performance: Understanding Garbage Collection Generations (Gen 0, Gen 1 & Gen 2) in C#

Introduction

In the previous article, we learned that the .NET Garbage Collector (GC) automatically removes unused objects from memory. But one important question remains:

Does the Garbage Collector scan every object in memory every time it runs?

If it did, large applications with millions of objects would become slow and inefficient.

To solve this problem, .NET uses an intelligent strategy called Generational Garbage Collection. Instead of treating every object equally, the Garbage Collector groups objects based on their age. Most newly created objects are short-lived, while only a few survive for a long time.

In this article, you'll learn what Generation 0, Generation 1, and Generation 2 are, why they exist, how objects move between them, and how this design improves the performance of .NET applications.

gc 02

Why Does .NET Use Generations?

Research shows that most objects created by an application live for only a short time.

For example:

  • Temporary strings

  • API request objects

  • Local collections

  • Validation objects

  • Temporary DTOs

These objects are often created, used, and discarded within milliseconds.

On the other hand, objects such as:

  • Application configuration

  • Cached data

  • Singleton services

  • Static objects

remain alive for much longer.

Instead of checking every object during every garbage collection cycle, .NET focuses on the newest objects first. This makes memory cleanup much faster.

What Are Garbage Collection Generations?

The managed heap is divided into three logical generations:

GenerationDescription
Generation 0 (Gen 0)Newly created objects
Generation 1 (Gen 1)Objects that survived one collection
Generation 2 (Gen 2)Long-lived objects that survived multiple collections

Think of generations as age groups rather than separate memory areas.

How Objects Move Between Generations

Every new object starts its journey in Generation 0.

If the object is no longer needed during a garbage collection, it is removed.

If it is still being used, it survives and moves to the next generation.

The process continues until the object eventually becomes unreachable and is collected.

Internal Flow

Create Object
      │
      ▼
Generation 0
      │
      ├── Not Referenced
      │        │
      │        ▼
      │    Removed by GC
      │
      ▼
Still Referenced
      │
      ▼
Generation 1
      │
      ├── Not Referenced
      │        │
      │        ▼
      │    Removed by GC
      │
      ▼
Still Referenced
      │
      ▼
Generation 2
      │
      ▼
Removed when no longer reachable

Understanding Generation 0

Generation 0 contains all newly created objects.

Example:

Employee employee = new Employee();

When this object is created, it is placed in Generation 0.

Generation 0 is collected most frequently because most temporary objects become unused very quickly.

Examples include:

  • Temporary strings

  • Method variables

  • Request objects

  • DTOs

  • LINQ query results

Since Gen 0 contains many short-lived objects, collections are usually fast.

Understanding Generation 1

Generation 1 acts as a bridge between short-lived and long-lived objects.

Objects reach Gen 1 only if they survive a Gen 0 collection.

These objects have lived longer than expected but are not yet considered long-lived.

Examples:

  • Objects shared across multiple method calls

  • Frequently reused collections

  • Temporary cache entries

Generation 1 is collected less often than Generation 0.

Understanding Generation 2

Generation 2 stores objects that have survived multiple garbage collections.

These objects usually remain in memory for a long time.

Examples include:

  • Application configuration

  • Cached data

  • Singleton services

  • Static collections

  • Shared application objects

Since Gen 2 often contains many important objects, collections are more expensive and happen less frequently.

Visual Journey of an Object

Application Starts
        │
        ▼
Create Employee Object
        │
        ▼
Generation 0
        │
        ▼
Still Needed?
   Yes ─────────► Generation 1
        │
        ▼
Still Needed?
   Yes ─────────► Generation 2
        │
        ▼
Reference Removed
        │
        ▼
Garbage Collector Removes Object

Practical Example

class Employee
{
    public string Name { get; set; }
}

Employee employee = new Employee();
employee.Name = "Ajay";

Initially:

Generation 0

Suppose the object is still needed after the first garbage collection.

It moves to:

Generation 1

If it continues to be referenced for a longer period, it is promoted again.

Generation 2

Eventually:

employee = null;

Once no references remain, the Garbage Collector removes the object during an appropriate collection cycle.

What About the Large Object Heap (LOH)?

Most objects are stored in the normal managed heap.

However, very large objects (typically 85 KB or larger) are allocated in a separate area called the Large Object Heap (LOH).

Examples include:

  • Large byte arrays

  • Large image buffers

  • Large collections

The LOH is managed differently because moving very large objects frequently would reduce performance.

We'll explore the Large Object Heap and its performance considerations in more detail in a later article.

Common Mistakes

Thinking objects are manually assigned to generations

Developers never choose the generation. The .NET runtime automatically manages object promotion.

Assuming Generation 2 is "better"

Generation 2 simply means an object has lived longer. It does not indicate higher importance or better performance.

Believing all generations are collected together

Most garbage collection cycles target only Generation 0. Gen 1 and Gen 2 are collected less frequently to improve performance.

Assuming promotion happens immediately

Objects move to a higher generation only after surviving a garbage collection.

Best Practices

  • Allow the Garbage Collector to manage object generations automatically.

  • Avoid creating unnecessary temporary objects in performance-critical code.

  • Reuse objects where appropriate to reduce allocation pressure.

  • Don't force collections using GC.Collect() unless there is a specific, well-understood reason.

  • Focus on writing clean, maintainable code rather than trying to manipulate generations.

Key Takeaways

  • Every new object starts in Generation 0.

  • Objects that survive a collection are promoted to higher generations.

  • Generation 0 is collected most frequently.

  • Generation 2 contains long-lived objects and is collected less often.

  • The generational design significantly improves the performance of .NET applications by avoiding unnecessary scans of long-lived objects.

  • Developers do not manually control object generations; the CLR manages the entire lifecycle.