In .NET, running applications store their state (data) in a heap (specifically objects <85k on the Small Object Heap (SOH) while larger objects are usually written to the Large Object Heap). When those objects are no longer required by the application, they need to be removed to make space for new objects. .NET deals with this through a process called Garbage Collection (GC).
The simplified model of the Garbage Collection process is as follows:
- Pause application execution
- Starting at the root of the application, follow all reference paths to discover which objects are reachable by the application. The root includes the stack, global & static objects, as well as the finalization references. Each object is only visited once to avoid issues with circular references.
- Remove all unreachable objects from the heap and free up the space.
- Compact the SOH.
- Resume application execution
Given that GC interrupts application execution, the shorter the GC runs, the better the user experience. One way to achieve this is to use a generational garbage collection algorithm. The idea behind it is that if an object has lived for a while it is probably used widely within the application and chances are that it will still be needed for a while longer. Usually there are far more short-lived objects in an application than long-lived ones, these being created within a small scope for a single task, so most young objects tend to be short-lived. In other words, the longer an object has lived, the longer it is likely to be needed, so cleaning young objects tends to be more fruitful than looking for older object to clean.
One way to track the age of an object is through Generations. (I capitalize Generations to indicate the special meaning in the context of GC). An object is created at Generation 0 and has its Generation incremented at a particular time. The .NET implementation uses 3 Generations, and the heap is organized into Generation 0, 1 and 2. When an object is created, it is placed in the Generation 0 section of the heap. After each garbage collection, it and all other surviving objects are promoted to the next Generation.
So when does GC happen?
Since most cleanup will involve younger objects, an efficient GC algorithm will clean younger objects more frequently. Cleaning older objects is less fruitful, so they should be cleaned comparatively rarely. The .NET GC cleans up the Generation 0 (youngest) heap most often, Generation 1 less often and Generation 2 rarely. Indicatively, Gen 0 is cleaned when it hits ~256K in size, Gen 1 around ~2MB and Gen 2 ~10MB, although these numbers are dynamic. Cleanup of older Generations always includes all younger Generations, so cleaning Generation 2 effectively means cleaning the entire heap.The actual timing of the GC is indeterminate. GC can also be triggered by code calling GC.Collect() – generally not recommended – or by the OS issuing a low memory notification.