Garbage Collection (GC) is a process in which the runtime environment (like the .NET CLR) automatically detects and frees memory that is no longer in use by the application. The purpose of GC is to manage memory allocation and deallocation, reducing the burden on developers(we) to manually manage memory and preventing memory leaks and fragmentation. It is part of the memory management system that helps ensure efficient use of memory in a .NET application. Garbage Collection is built into the .NET runtime itself — specifically, it's part of the Common Language Runtime (CLR).We do not need to install a NuGet package or add a reference to use garbage collection. It's:
- Built into the core .NET runtime
- Available in .NET Framework, .NET Core, and .NET 5/6/7/8+
![]()
Before exploring Garbage Collection (GC), it's important to first understand what memory management is and why it's crucial in software development.
Memory Management
Memory management ensures that memory is allocated when needed and freed up when no longer used. Proper memory management helps avoid memory leaks, which can slow down or crash applications.
Manual Memory Management: In languages like C and C++, developers are responsible for both allocating memory (e.g., using malloc
) and freeing memory (e.g., using free
). This requires careful tracking of memory to avoid errors like memory leaks (forgotten free
calls) or double freeing.
Automatic Memory Management (Garbage Collection): In .NET, memory management is handled automatically by the CLR (Common Language Runtime) using Garbage Collection (GC). We don't need to manually free memory, reducing the risk of memory leaks and simplifying application development. The garbage collector automatically identifies objects that are no longer needed (those that no longer have references) and removes them. We do not have to worry about explicitly freeing objects or managing memory, as the GC takes care of it in the background.
The architecture of the .NET Garbage Collection (GC)
Memory Segments: The memory managed by the GC is divided into several segments for more efficient management:
- Stack memory is automatically managed for temporary data like local variables and function calls. It’s fast and cleared when a method finishes executing.Stack memory is fast and short-lived. In simple words Stack memory is like a checkout counter at a supermarket, where items (local variables) are quickly added and removed as customers (methods) finish shopping. Once the customer (method) is done, the items (local variables) are cleared.
- Heap memory is used for objects and is dynamically allocated, with the garbage collector (GC) automatically cleaning up unused objects. Heap memory can hold long-lived objects but requires GC to manage deallocation. The CLR handles both types, ensuring automatic memory management. This allows us to focus on logic, not memory management. In simple words Heap memory is like a library storage room where books (objects) are stored until they are no longer needed. The garbage collector (library staff) periodically checks and removes unused books (objects).
.NET Generations and Large Object Heap (LOH): The Garbage Collection uses a generational approach, dividing objects into different generations to optimize garbage collection and reduce performance overhead.
- Generation 0 This generation contains newly created objects. These objects are typically short-lived, meaning they are created and then quickly discarded (like temporary data or objects used within methods).Generation 0 is collected more frequently because objects here are often discarded soon after they are used. Like Generation 0 as the newspaper in our house. we buy a newspaper in the morning, read it, and throw it away at the end of the day. The newspaper (newspaper object) was just briefly used and discarded.
- Generation 1 Objects that survive one or more garbage collection cycles in Generation 0 are promoted to Generation 1. These objects have a longer lifespan, but they are still considered short-term. Generation 1 is collected less frequently than Generation 0 but more frequently than Generation 2. Imagine we have a monthly planner. we keep it for a month or two, but eventually, we no longer need it and throw it away. The planner is more long-term than a newspaper, but it’s still not something you hold onto forever.
- Generation 2 holds the oldest objects, which have survived multiple garbage collection cycles. These are typically long-lived objects, such as configurations, global data, or objects that are kept throughout the life of the application.Generation 2 is collected the least frequently because these objects are assumed to have a long lifespan. Generation 2 is like a favorite chair in your living room. It’s been with you for years, and you plan to keep it for a long time because it’s comfortable and reliable. You don’t get rid of it quickly, even though it’s old. It’s something you expect to stay around and be used for many years.
- Large Object Heap (LOH) is a special area where objects larger than 85,000 bytes are stored. These objects are considered expensive to collect because of their size. The LOH is collected less frequently because collecting large objects is more costly and can negatively impact performance. Large Object Heap (LOH) is like a big sofa in your living room. It’s large, takes up a lot of space, and while you use it every day, you don’t move or clean it as often as smaller furniture. Since it’s heavy and difficult to move, you clean it less frequently, only when absolutely necessary.
Manual Garbage Collection Methods in .NET
While .NET handles garbage collection automatically, these methods allow us to Interact, observe when needed. Optimize performance in edge cases. Clean up unmanaged resources deliberately. When we refer to classes like System.GC
in .NET, we're accessing functionality provided by the Base Class Library (BCL)
Method |
Purpose |
When to Use |
GC.Collect() |
Forces a garbage collection |
Demo, testing, memory pressure analysis—not recommended in production |
GC.GetTotalMemory(bool forceFullCollection) |
Returns estimated allocated memory |
Monitor memory usage with or without collection |
GC.WaitForPendingFinalizers() |
Blocks until finalizer queue is empty |
Ensures finalizers run before next steps |
GC.SuppressFinalize(object obj) |
Prevents finalizer from running |
When Dispose() already cleaned resources |
GC.ReRegisterForFinalize(object obj) |
Re-enables finalizer after suppression |
Rare cases where cleanup needs to be restored |
GC.GetGeneration(object obj) |
Returns the generation of an object |
Debugging or tuning GC behavior |
GC.MaxGeneration |
Gets the highest supported generation |
Typically returns 2 |
GC.AddMemoryPressure(long bytesAllocated) |
Tells GC about unmanaged memory usage |
Useful in wrapper classes around unmanaged resources |
GC.RemoveMemoryPressure(long bytesReleased) |
Decreases the pressure previously added |
Complements AddMemoryPressure() |
Automatic Garbage Collection in .NET
This is the default, built-in behavior of .NET's Common Language Runtime (CLR). Passive observer. we write our code, and GC handles memory for us.
Think of this as our smart home assistant cleaning the house when it's needed.
Automatic Garbage Collection Phases
There are three main phases of garbage collection: Marking, Sweeping, and Compacting
![]()
- Marking Phase: Identify the items in the room that are still in use (live objects) and separate them from the unused ones.
- Sweeping Phase: Remove the unused items.
- Compacting Phase: Rearrange the remaining items to make better use of the space.
How the Automatic Garbage Collection Works
Example code
using System;
public class HouseCleaningSimulation
{
public class Furniture
{
public string Name { get; set; }
public Furniture(string name)
{
Name = name;
}
}
public static void Main()
{
Console.WriteLine("Phase 1: Marking - Identifying live objects");
Furniture sofa = new Furniture("Sofa");
Furniture chair = new Furniture("Chair");
Furniture table = new Furniture("Table");
Console.WriteLine($"Initial memory usage (before any GC): {GC.GetTotalMemory(false)} bytes");
chair = null;
Console.WriteLine($"\nMemory usage after setting chair to null (object becomes unreachable): {GC.GetTotalMemory(false)} bytes");
Console.WriteLine("\nPress Enter to continue and simulate automatic GC phases...");
Console.ReadLine();
Console.WriteLine($"Final memory usage after automatic GC cleanup: {GC.GetTotalMemory(false)} bytes");
Console.WriteLine("\nHouse cleaning is complete!");
}
}
Marking Phase
- In this phase, the garbage collector identifies live objects—those still in use.
- In the code: The objects
sofa
, chair
, and table
are created. The GC marks all objects that are reachable.
- However, after setting the
chair
to null
, it becomes unreachable. The GC will mark this object as dead (garbage).
Sweeping Phase
- In this phase, the GC removes unused objects from the heap.
- In the code: After marking, the
chair
object becomes eligible for garbage collection. The garbage collector will automatically remove it from memory.
- This happens automatically during the application's execution without needing to explicitly call
GC.Collect()
.
Compacting Phase
- After sweeping up unused objects, the GC will compact the heap, moving the remaining live objects together to reduce memory fragmentation.
- In the code: The GC automatically handles this as part of the memory management, moving the remaining objects (such as
sofa
and table
) closer together in memory, which optimizes space for future object allocation.
Why We Use GC and What Happens Without GC
- Automatically reclaims memory from objects no longer in use.
- Prevents memory leaks, dangling pointers, and crashes.
- Boosts overall application stability and performance.
If .NET didn’t have garbage collection:
- We need to manually free memory for every object.
- Forgotten cleanups would lead to memory leaks, degraded performance, and eventual app crashes.
- Complex applications would require intricate memory tracking logic, increasing the chance of bugs.
Conclusion
As I've explored Garbage Collection (GC) in .NET, it's clear that it's a powerful and automated system that efficiently manages memory—freeing developers to concentrate on building features instead of battling manual memory management.