Garbage Collection in C#๐๐ค๐๐๐
It becomes imperative to note that automatic memory management is made possible by Garbage Collection in .NET Framework. When a class object is created at runtime, certain memory space is allocated to it in the heap memory. However, after all the actions related to the object are completed in the program, the memory space allocated to it is a waste as it cannot be used. In this case, garbage collection is very useful as it automatically releases the memory space after it is no longer required. Garbage collection will always work on Managed Heap and internally it has an Engine which is known as the Optimization Engine. Garbage Collection occurs if at least one of multiple conditions is satisfied. These conditions are given as follows:
- If the system has low physical memory, then garbage collection is necessary.
- If the memory allocated to various objects in the heap memory exceeds a pre-set threshold, then garbage collection occurs.
- If the GC.Collect method is called, then garbage collection occurs. However, this method is only called under unusual situations as normally garbage collector runs automatically.
Phases in Garbage Collection๐๐๐
There are mainly 3 phases in garbage collection. Details about these are given as follows:
- Marking Phase: A list of all the live objects is created during the marking phase. This is done by following the references from all the root objects. All of the objects that are not on the list of live objects are potentially deleted from the heap memory.
- Relocating Phase: The references of all the objects that were on the list of all the live objects are updated in the relocating phase so that they point to the new location where the objects will be relocated to in the compacting phase.
- Compacting Phase: The heap gets compacted in the compacting phase as the space occupied by the dead objects is released and the live objects remaining are moved. All the live objects that remain after the garbage collection are moved towards the older end of the heap memory in their original order.
Fundamental of Finalization๐คทโโ๏ธ๐คทโโ๏ธ
When a new object is created, the memory is allocated in the managed heap. If newly created object have a Finalize() method or a destructor then a pointer pointing to that object is put into the finalization queue. Basically, finalization queue is an internal data structure that is controlled and managed by the GC. Hence each pointer in finalization queue points to an object that have its Finalize method call before the memory is reclaimed.
In the below fig. the managed heap contains 10 objects and objects 2,3,5,6,and 10 also contains the Finalize method. Hence pointers to these objects were added to the finalization queue as shown below:
Heap Generations in Garbage Collection๐๐ด
The garbage collection groups objects on the managed heap into generations. This improves the performance of the garbage collector (GC), since it typically collects objects in Generation 0 (newest), only moving to older generations if necessary.
The heap memory is organized into 3 generations so that various objects with different lifetimes can be handled appropriately during garbage collection. The memory to each Generation will be given by the Common Language Runtime(CLR) depending on the project size. Internally, Optimization Engine will call the Collection Means Method to select which objects will go into Generation 1 or Generation 2.
- Generation 0 : All the short-lived objects such as temporary variables are contained in the generation 0 of the heap memory. All the newly allocated objects are also generation 0 objects implicitly unless they are large objects. In general, the frequency of garbage collection is the highest in generation 0.
- Generation 1 : If space occupied by some generation 0 objects that are not released in a garbage collection run, then these objects get moved to generation 1. The objects in this generation are a sort of buffer between the short-lived objects in generation 0 and the long-lived objects in generation 2.
- Generation 2 : If space occupied by some generation 1 objects that are not released in the next garbage collection run, then these objects get moved to generation 2. The objects in generation 2 are long lived such as static objects as they remain in the heap memory for the whole process duration. This can be illustrated as shown below:
Note: Garbage collection of a generation implies the garbage collection of all its younger generations. This means that all the objects in that particular generation and its younger generations are released. Because of this reason, the garbage collection of generation 2 is called a full garbage collection as all the objects in the heap memory are.released. Also, the memory allocated to the Generation 2 will be greater than Generation 1โs memory and similarly the memory of Generation 1 will be greater than Generation 0โs memory(Generation 2 > Generation 1 > Generation 0).A program that demonstrates the number of heap generations in garbage collection using the GC.MaxGeneration property of the GC class is given as follows:
A program that demonstrates the number of heap generations in garbage collection using the GC.MaxGeneration property of the GC class is given as follows:
In the above program, the GC.MaxGeneration property is used to find the maximum number of generations that are supported by the system i.e. 2. If you will run this program on online compilers then you may get different outputs as it depends on the system.
Methods in GC class๐
GC.GetGeneration() Method : This method returns the generation number of the target object. It requires a single parameter i.e. the target object for which the generation number is required. A program that demonstrates the GC.GetGeneration() method is given as follows:
GC.GetTotalMemory() Method: This method returns the number of bytes that are allocated in the system. It requires a single boolean parameter where true means that the method waits for the occurrence of garbage collection before returning and false means the opposite. A program that demonstrates the GC.GetTotalMemory() method is given as follows:
GC.Collect() Method : Garbage collection can be forced in the system using the GC.Collect() method. This method requires a single parameter i.e. number of the oldest generation for which garbage collection occurs. A program that demonstrates the GC.Collect() Method is given as follows:
How To Force Garbage Collection in C#๐ฏ๐ฎ๐๐ค
Yes it is possible to force garbage collector in C# to run by calling Collect() method
This is not considered a good practice because this might create a performance over head. Collect() Forces an immediate garbage collection of all generations.
Collect(Int32)Forces an immediate garbage collection from generation 0 through a specified generation.
Key points about Garbage Collector๐ค๐
All objects in the heap are allocated from one contiguous range of memory address and heap is divided into generations so that it is easy to eliminate the garbage objects by looking at only a small fraction of the heap.
Gen 0 and Gen 1 occupy a single segment known as the ephemeral segment. Gen 2 is a set of further segments and the large object heap is yet another group of segments.
Almost, all objects with-in a generation are of the same age. The newest objects are created at higher memory address while oldest memory objects are at lowest memory address with in the heap.
The allocation pointer for the new objects marks the boundary between the allocated and free memory.
Periodically the heap is compacted by removing the dead objects and sliding up the live objects towards the lower memory address end of the heap as shown in above fig.
The order of objects (after memory reclaims) in memory remains the same as they were created.
There are never any gaps among the objects in the heap.
Only some of the free memory is committed when required and more memory is acquired from the OS in the reserved address range.
Summary๐คทโโ๏ธโ
In Summary, Garbage Collection succeeds in allocating objects efficiently on the heap memory using the generations of garbage collection. Manual freeing of memory is not needed as garbage collection automatically releases the memory space after it is no longer required. Garbage collection handles memory allocation safely so that no objects use the contents of another object mistakenly. The constructors of newly created objects do not have to initialize all the data fields as garbage collection clears the memory of objects that were previously released.