Garbage Collection is an automated way of memory management. The first time it is initiated by American computer scientist John McCarthy in 1959 in Lisp to ease memory management. The Garbage Collection intends to collect objects which aren’t being used by the program anymore and reclaim the memory occupied by those objects and the main advantage of this process is to relieve the developer from dealing with memory management manually. However, it comes with a performance trade-off. Unless the resources are being used wisely, the Garbage Collection process might take a significant part of the total process time.
A Garbage Collection mechanism is provided in many languages. Thus its internal mechanism or logic might differ from language to language. But the main idea is the same for all. In this article, we will have a deep look at the internal dynamics of the Garbage Collection process in the .NET environment to gain a better understanding.
Table of contents of Part 1
Table of contents of Part 2
- The Components of the Garbage Collector
Table of contents of Part 3
Table of contents of Part 4
Garbage Collector in .NET Framework
The .NET Framework consists of two main sections that provide tools and technologies to develop Web, Mobile, and Desktop applications. The Garbage Collector is part of the CLR (Common Language Runtime) module. Besides the CLR providing various tools and services, it is the module that is responsible for the execution of the Garbage Collector. In the CLR, there is also another component that works closely with the Garbage Collector. This component is „the JIT Compiler“.
Garbage Collector needs to build an object tree to find unused objects and reclaim the memory occupied by those objects. To build this object tree, it needs some information about the executing program such as static and local variables, GC Handles, or F-Reachable queue which we call ‘Root Objects’. We are going to look into the details of the ‘Root Objects’ in part 2 of this series.
The static and local variables are provided to the Garbage Collector by the JIT Compiler. As the above figure shows us what the JIT compiler does and how it takes place during code compilation. The main job of the JIT compiler is to take the half compiled code (IL Code) by the compiler and compile it to machine-specific code. During this compilation process, it provides all variables (local and static) to the Garbage Collector to help build the object tree that it needs to do its job.
The Components of the Garbage Collector
The Garbage Collector consists of five components and the first of these components we will look at is Generations.
Generations are segmented memory sections in the Managed Heap. They are used to improve the collection performance. In .NET, the Garbage Collector divides the managed heap into three generations. The idea behind the generational model is that if an object is new, it is expected to die sooner. If an object is old, it is expected to die later.
It is the memory region where all new objects are created in. Relying on the idea that new objects are expected to die sooner, the Garbage Collector performs on this region frequently. The size of this region is limited to between 256K and 4MB. As additional information, Gen-0 refers to the L2 cache of the CPU. The Gen-0 collection is a rather quick operation since it is small.
After the run of the Garbage Collector on Gen-0, survived objects are promoted into this region. Garbage Collector performs on this region slightly less than Gen-0. The size of this region is similar to Gen-0 and between 512K and 4MB.
Survived objects from Gen-1 are promoted to Gen-2. Objects in this region are considered old objects and they won’t be collected soon. That’s why the Garbage Collector rarely performs in this region. The size of this region is not limited. It can use the entire memory space of the .NET process. Therefore, the memory size might be up to 2GB on a 32-bit system and 8TB on a 64-bit system.
LOH (Large Object Heap)
Apart from generations, a Large Object Heap is a region in memory that stores object larger than 85K. It can be considered a part of the Gen-2 region. Survived objects in the LOH are not compacted which means the LOH becomes fragmented over time.
Before closing this section, there is another important point that must be mentioned. Garbage collection on Gen-0 and Gen-1 is a partial garbage collection because it doesn’t clean the whole memory, only Gen-0, and Gen-1. But the collections on Gen-2 are named as full garbage collection since they trigger garbage collection on Gen-1 and Gen-0 as well. The First Gen-2 collection is performed, after this, the Gen-1 and Gen-0 collections take place. If there still isn’t enough memory for new allocations, then Garbage Collector raises the OutOfMemoryException.
The lack of Gen-1 is a Mid-life crisis. A mid-life crisis occurs when temporary objects that should have been collected in Gen-1, are prompted to Gen-2 and die shortly afterward.
During full garbage collection, Garbage Collector has to pass through all objects in the heap. This process is very expensive and might have a big impact on system resource consumption.