Garbage Collection (GC) in .NET
Memory management is one of the most important concepts in .NET. Developers often say “the Garbage Collector handles memory for you,” but few can clearly explain how it works, why it exists, and what you can do to work with it instead of against it.
This guide breaks down .NET’s Garbage Collection (GC) in a simple, practical way — with definitions, diagrams, examples, and real-world scenarios.
What Is Garbage Collection?
Garbage Collection (GC) is an automatic memory management system in .NET.
Its job is to:
- Allocate memory for new objects
- Track which objects are still in use
- Free memory for objects that are no longer needed
- Prevent memory leaks
- Reduce developer errors like dangling pointers
In short: GC keeps your application healthy by cleaning up unused objects automatically.
How GC Works in .NET
The .NET GC uses a generational, mark‑and‑compact algorithm.
Let’s break that down.
1. Generations (Gen 0, Gen 1, Gen 2)
Objects are grouped into generations based on their lifetime.
Generation 0
- Newly created objects
- Collected frequently
- Fastest to clean
Generation 1
- Surviving objects from Gen 0
- Medium‑lived objects
Generation 2
- Long‑lived objects
- Large objects
- Cleaned infrequently
Why generations?
Most objects die young.
So GC optimizes for that.
2. Mark Phase
GC pauses the application briefly and marks all objects that are still reachable.
Reachable means:
- Local variables
- Static references
- Objects referenced by other objects
Everything else is considered garbage.
3. Compact Phase
After marking, GC compacts memory by moving surviving objects together.
This reduces fragmentation and improves performance.
4. Large Object Heap (LOH)
Objects > 85 KB go to the Large Object Heap.
- Not compacted often (expensive to move large objects)
- Can cause fragmentation
- Avoid unnecessary large allocations
When Does GC Run?
GC runs automatically when:
- Memory is low
- Gen 0 fills up
- The system is under pressure
- You explicitly call
GC.Collect()(not recommended)
Should You Call GC.Collect()?
Almost never.
Calling it manually:
- Forces a full collection
- Pauses your app
- Hurts performance
- Breaks GC’s optimization logic
Use it only in rare scenarios:
- After a massive one‑time memory release
- In controlled environments (e.g., game engines)
How to Work With GC (Best Practices)
✔️ Use using statements for disposable objects
using (var stream = new FileStream("data.txt", FileMode.Open))
{
// work with stream
}
This ensures deterministic cleanup.
✔️ Avoid unnecessary large object allocations
var bigArray = new byte[100_000]; // Goes to LOH
Reuse buffers when possible.
✔️ Prefer Span<T> and Memory<T> for high-performance scenarios
These avoid heap allocations.
✔️ Avoid long-lived references
If you keep references alive, GC cannot collect them.
✔️ Use dependency injection wisely
Singletons live for the entire app lifetime — avoid storing large objects inside them.
Real‑World Scenarios
Scenario 1: Web API handling thousands of requests
Short-lived objects → Gen 0
GC handles them efficiently.
Scenario 2: Image processing
Large byte arrays → LOH
Avoid repeated allocations; use pooling.
Scenario 3: Background services
Long-lived services → Gen 2
Be careful with memory leaks.
Interview‑Ready Summary
- GC automatically manages memory in .NET
- Uses generational collection (Gen 0, 1, 2)
- Uses mark and compact algorithm
- LOH stores large objects
- Avoid calling
GC.Collect()manually - Use
using, pooling, and DI best practices
This explanation shows you understand both the theory and the practical implications.
✅ Bonus thought
Is IDisposable Related to Garbage Collection? Clearing the Confusion
Many developers — especially those new to .NET — assume that IDisposable is part of the Garbage Collector. It’s a common misunderstanding, but the two concepts solve different problems.
Garbage Collection handles managed memory
GC automatically frees memory for objects stored on the managed heap.
Examples:
- Classes
- Strings
- Arrays
- Delegates
GC decides when to clean them up.
IDisposable handles unmanaged resources
IDisposable exists because GC cannot clean up unmanaged resources, such as:
- File handles
- Database connections
- Network sockets
- OS handles
- Streams
- Native memory
These require deterministic cleanup, which is why we use:
using (var stream = new FileStream("data.txt", FileMode.Open))
{
// work with stream
}
How they relate
- GC cleans up managed memory
-
IDisposablecleans up unmanaged resources - Finalizers (
~ClassName) act as a safety net, but they are expensive - The
usingstatement ensures cleanup happens immediately, not “whenever GC runs”
The rule of thumb
GC ≠ Dispose
GC frees memory
Dispose frees resources
This distinction is crucial in interviews and real-world systems.
Top comments (0)