Measuring the heap size at a fixed interval (e.g., every second) can be completely misleading because a lot can happen between measurements.For example, consider measuring the heap size once per second. In this scenario, you can see the GC happen because the heap size drops.
Table 1: A Visible GC
| Second | Action | Heap Size at End of Second |
|---|---|---|
| 1 | allocated 1 GB | 1 GB |
| 2 | allocated 2 GB | 3 GB |
| 3 | allocated 0 GB | 3 GB |
| 4 | GC happens, then allocated 1 GB | 1.5 GB |
| 5 | allocated 3 GB | 4.5 GB |
However, in the next scenario, a GC happens but is immediately followed by more allocations. From the measurements alone, it’s impossible to tell that a collection even occurred.
Table 2: A Hidden GC
| Second | Action | Heap Size at End of Second |
|---|---|---|
| 1 | allocated 1 GB | 1 GB |
| 2 | allocated 2 GB | 3 GB |
| 3 | GC happens, then allocated 2 GB | 3 GB |
| 4 | allocated 1 GB | 4 GB |
Warning
Point-in-time heap snapshots can hide collections.
The only reliable measurement is before a GC starts and after it finishes.
The Allocation Budget: Your Spending Limit 💳
The allocation budget is the amount of memory a program is allowed to allocate before a GC is triggered.
Tip
Think of it as a credit card limit. Once you’ve spent the full amount, you have to stop and deal with the bill (the GC).
This budget is conceptually the difference between the heap size after the last GC and the heap size right before the next one.
While technical details like object pinning can complicate the exact number, the core idea remains: it’s the amount of allocation “allowed” between cleanups.
Generational GC: A House with Three Rooms 🏠
The .NET GC is generational, dividing the heap into three areas based on the age of objects:
- Gen 0 (The Entryway): All new, small objects start here. This room is for short-lived items and gets cleaned frequently with quick ephemeral GCs.
- Gen 1 (The Hallway): Objects that survive a Gen 0 cleanup are moved here. It acts as a buffer.
- Gen 2 (The Storage Room): Objects that survive a Gen 1 cleanup are promoted here. It holds long-lived objects, and cleaning it is an expensive full GC, so it happens much less often.
Important
An object can only be collected when its generation is being cleaned.
A Gen 2 object will survive indefinitely, no matter how many Gen 0 or Gen 1 GCs run.
Visualization 🖼️
Here’s a simple diagram showing the object life cycle through generations:
flowchart LR A[New Object in Gen 0 - Entryway] -->|Survives GC| B[Gen 1 - Hallway] B -->|Survives GC| C[Gen 2 - Storage Room] A -->|Collected| X((Freed)) B -->|Collected| X C -->|Collected in Full GC| X
