Garbage collectors are very deep technical topics that rely on observations of trends in software workloads and careful heuristics and engineering to achieve consistent high throughput. A major design point that many advanced GCs employ is called “Generations”.
In .NET we have a tracing garbage collector which needs to check every object, and every child object that is referenced by that parent object, and all the grandchild object and so on, until it realizes that there are a bunch little object islands that are not connected to the rest of the object continent. Those islands are marked as dead and are recycled by the GC.
The basic premise of generational collection is that object lifetimes exhibit temporal locality. Which heavily paraphrasing means that newly allocated objects typically die off very quickly. But sometimes a newly allocated object survives a GC sweep and is still alive. These survivor objects are statistically more likely to survive another GC sweep in the near future. Therefore, we should not bother trying to collect them on the next pass because it will likely be a waste of time.
So instead, once an object and its connected descendants survive a GC sweep, we promote them into a higher “generation”, effectively skipping the cost of tracing their liveness the next time we need to sweep.
Now obviously, this would create a memory leak since we would only ever add more objects to the survivor generation. So we introduce a next level garbage collection called a Gen1 collection that also includes these survivor objects. We only run the Gen1 collections every once in a while, because statistically, we expect we will have to do a lot of tracing work for not very much payoff because we expect many survivors of the Gen1 sweep. But perhaps we will catch some anyway, so we try it.
As you might have guessed, if there are objects that survive a Gen1 sweep, then we promote them to become an even higher generation (Gen2) what we only sweep on a very infrequent basis. The big idea here is to try to optimize the effectiveness of our GC time by only spending cycles to trace objects that we believe will actually have a good statistical change of being dead and therefore profitable to sweep.
Now after decades of building garbage collectors, the industry has basically concluded that 3 generations is enough and that adding more generations doesn’t really help much. But there is a twist! Some garbage collectors, including .NET’s, will use a Gen2 collection to also perform other complex and time-consuming tasks such as “Compaction” which is where the GC rearranges the objects, so they don’t have gaps in between them, much like defragmenting a hard disk back in the day 🧓. So Gen2 collections tend to be much more time consuming than Gen0s or Gen1s, and a GC will typically only perform them when it feels like it is running out of memory.
Garbage collectors also have other tricks similar to this, such as using a “Large Object Heap” that very large, very expensive object are specially allocated to because they can take a lot of time to trace and eventually compact. .NET also employes new Pinned Object Heap and Frozen Object Heap which to also try to avoid wasting time on objects that will likely not be successfully collected during a normal sweep.
The Pinned Object Heap is a special place where you can create objects that the .NET GC and runtime promise to never compact or rearrange. This is important when sharing memory with other systems that don’t understand how the .NET memory system works. These systems generally expect that when they are given a pointer to an object, that it will not suddenly move to another place at random. This “pinned” objects, cannot be moved during a compaction phase, so the GC puts them in a special place to avoid wasting time trying to move them before realizing that it isn’t allowed to.
The Frozen Object Heap is similar to the pinned object heap, except it is for immortal objects that the GC know will never be collection, such as `static readonly` objects.
There is a lot more to it than this, but that is the
highlights. Let me know if there is something I didn’t cover that you would
like me to.