# C# Garbage Collection

### The Two Pillars of .NET Memory: Stack and Heap

In C#, a running program uses two primary memory segments: the Stack and the Heap. Understanding their distinct roles is fundamental to comprehending Garbage Collection.

#### **1\. Stack Memory**

The stack is a fast, LIFO (Last-In, First-Out) memory region that stores **value types** (e.g., `int`, `bool`, `struct`) and **references** (pointers) to objects on the heap.

* **Key Characteristics**: Memory is allocated and deallocated automatically as methods are called and completed, making it incredibly fast.
    
* **Worst Case**: A `StackOverflowException`, which occurs when the stack runs out of memory, often due to excessively deep or infinite recursion.
    

#### **2\. Heap Memory**

The heap is a dynamic, non-contiguous memory region used for storing **reference types** (e.g., `class` instances, `string`, `array`).

* **Key Characteristics**: Memory on the heap must be explicitly allocated and deallocated. In C#, this is handled automatically by the Garbage Collector.
    
* **Worst Case**: A **memory leak**, where objects that are no longer needed are still referenced, preventing the Garbage Collector from freeing their memory.
    

---

### How Garbage Collection Works: A Generational Approach

The .NET Garbage Collector is the automatic memory manager for the heap. It uses a **generational approach** to optimize performance, operating on the principle that most objects are short-lived.

#### **The Managed Heaps**

The managed heap is physically divided into two main areas:

* **Small Object Heap (SOH)**: For objects smaller than 85 KB. This is the generational part of the heap.
    
* **Large Object Heap (LOH)**: For objects 85 KB and larger. This heap is handled separately.
    

#### **Generations of the SOH**

The SOH is divided into three generations to manage objects of different lifetimes:

* **Generation 0**: This is where all **newly created objects** are allocated. The GC collects this generation most frequently, as most objects are expected to become garbage here.
    
* **Generation 1**: This acts as a buffer. Objects that survive a Gen-0 collection are **promoted** to this generation.
    
* **Generation 2**: This is where **long-lived objects** reside. Objects that survive a Gen-1 collection are promoted here. This generation is collected the least frequently.
    

#### **The Garbage Collection Process**

A full garbage collection (a Gen-2 collection) involves several distinct steps:

1. **Suspension**: All application threads are temporarily paused in a "Stop-the-World" event to ensure the memory state doesn't change during collection.
    
2. **Root Identification**: The GC identifies all "roots"—references to objects from the stack, static fields, and registers.
    
3. **Marking**: Starting from the roots, the GC traverses the object graph, marking every reachable object as "live." All objects not marked are considered "dead."
    
4. **Relocation (Compaction)**: For the SOH, the GC physically moves all live objects to the beginning of the memory segments, packing them together. This eliminates fragmentation and creates a large, contiguous block of free space.
    
5. **Pointer Updates**: The GC updates all references in the roots and other live objects to point to the new memory addresses of the relocated objects.
    
6. **Sweeping (LOH)**: For the LOH, compaction is not performed by default due to the high cost of moving large objects. Instead, dead objects leave "holes" in the memory, and the GC adds these blocks to a **free list** for later reuse.
    
7. **Resumption**: The GC unpauses the application threads, and the program resumes execution.
    

### GC Triggers and Collection Flow

The GC doesn't run randomly. It is triggered under specific conditions, and the collection process follows a predictable, escalating pattern:

#### **Gen-0 Collection**

This is the most common and fastest collection.

* **Trigger**: The most frequent trigger is when a new object allocation exceeds the predefined budget for Gen-0. It can also be triggered by a system-wide low memory event or a manual `GC.Collect(0)` call.
    
* **Process**: The GC performs a collection on Gen-0. Live objects are **promoted** to Gen-1, and the entire Gen-0 memory segment is **wiped clean**. Gen-0 is fully freed.
    

#### **Gen-1 Collection**

This is the secondary collection, which is more expensive than Gen-0 but less than Gen-2.

* **Trigger**: It is triggered only if a Gen-0 collection does not free up enough memory to satisfy the current allocation request.
    
* **Process**: The GC collects both Gen-0 and Gen-1. Live objects from Gen-0 are promoted to Gen-1, and live objects from Gen-1 are promoted to Gen-2. After the process, both Gen-0 and Gen-1 are completely empty.
    

#### **Gen-2 Collection (Full GC)**

This is the most time-consuming and least frequent collection.

* **Trigger**: A Gen-2 collection is a last resort, triggered when:
    
    * A Gen-1 collection fails to free up enough memory.
        
    * An allocation on the **Large Object Heap (LOH) exceeds its threshold**.
        
    * The system detects a critical low memory condition.
        
    * A full collection is manually forced with `GC.Collect()`.
        

#### **Large Object Heap (LOH) Collection**

* **Trigger**: The LOH is only collected during a **Gen-2 collection**. It is not part of the Gen-0 or Gen-1 collection cycles.
    
* **Process**: The GC handles the LOH using a Mark-and-Sweep algorithm, as it typically does not compact this heap.
    

### Important Notes and Key Takeaways

* **Contiguous Memory**: The .NET runtime allocates memory for a **single object** in a **single, contiguous block**. This design is critical for fast allocation and efficient memory locality. It also enables the GC's compaction process.
    
* **Unmanaged Resources**: The GC only manages the **managed heap**. It does not handle unmanaged resources like file handles or network connections. You must explicitly release these using the `IDisposable` interface and the `using` statement.
    
* **Static Events and Leaks**: Be mindful of long-lived objects (like static classes) holding references to short-lived objects (like windows or services) through events. If you don't unsubscribe from the event, the GC will never collect the short-lived object, leading to a memory leak.
    
* **Manual GC**: Avoid calling `GC.Collect()`. The GC's automatic heuristics are highly tuned and almost always more efficient than a manual call. Manually forcing a collection can be an expensive operation that can degrade performance.
