Garbage collectors and memory management go hand in hand in every object-oriented language. As a developer, getting to know how memory management works behind the scenes is really important. Let’s try to understand how the garbage collector helps to distribute and retain memory over the lifecycle of an application.
Garbage collector (GC) serves as an automatic memory manager. The garbage collector manages the allocation and release of memory for an application. When a class object is created at runtime, certain memory space is allocated to it in the heap memory. All the behaviour of that objects can be done in the allotted memory in the heap.
GC can eliminate common problems, such as forgetting to free an object and causing a memory leak or attempting to access memory for an object that’s already been freed. It also helps in following ways –
- Faster object creation as Operating system level synchronisation is no more needed for each object. Object Allocation takes some memory and increases the offset.
- When an object is not required, garbage collector reuses the object’s memory for further allocation
Back to Basics
Each process has its own, separate virtual address space. All processes on the same computer share the same physical memory and the page file, if there is one.
As a developer we work only with virtual address space and never manipulate physical memory directly. The garbage collector allocates and frees virtual memory for you on the managed heap.You can run out of memory if there isn’t enough virtual address space to reserve or physical space to commit.
How memory management works
The runtime sets a contiguous area of address space for the process when you initialise a new process. This address space that is reserved is called the managed heap. The managed heap holds a pointer to the address where it will assign the next object in the heap. Initially, this pointer is set to the base address of the managed heap. On the managed heap, all reference types are allocated. When the first reference type is generated by an application, memory is assigned to the type at the base address of the managed heap. When the application produces the next object, in the address space directly after the first object, the garbage collector allocates memory for it. The garbage collector continues to reserve space for new items in this fashion, as long as address space is available.
Allocating memory from the managed heap is faster than unmanaged memory allocation. because the runtime allocates memory for an object through including a cost to a pointer, it’s nearly as speedy as allocating memory from the stack. Further, because new objects that are allotted consecutively are stored contiguously in the managed heap, an application can get entry to the objects quickly.
Memory release techniques
The garbage collector’s optimising engine determines the fine time to perform a collection based on the allocations being made. when the garbage collector performs a collection, it releases the memory for objects which might be no longer being utilised by an application. It determines which objects are now not being utilised by inspecting the application’s roots. An application’s roots include static fields, local variables on a thread’s stack, CPU registers, GC handles, and the finalize queue. Each root either refers to an object on the managed heap or is set to null. The garbage collector can ask the rest of the runtime for these roots. the usage of this listing, the garbage collector creates a graph that incorporates all the items which are accessible from the roots as shown in the diagram below -
Objects that aren’t inside the graph are unreachable from the application’s roots. the garbage collector considers unreachable objects and releases the memory allotted for them. At some stage in a collection, the garbage collector examines the managed heap, looking for the blocks of address space occupied by unreachable objects. As it discovers each unreachable object, it makes use of a memory-copying function to compact the accessible objects in memory, liberating up the blocks of address spaces allotted to unreachable objects. as soon as the memory for the reachable items has been compacted, the garbage collector makes the essential pointer corrections so that the application’s roots point to the items of their new locations. It also positions the managed heap’s pointer after the remaining reachable object.
- There is poor spatial memory in the system. Either the low memory warning from the OS or low memory as shown by the host senses this.
- The memory used on the managed heap by assigned items surpasses an appropriate threshold. As the mechanism runs, this threshold is changed continuously.
What is Managed Heap ??
Till now we understood basics of how GC allocates and reclaims memory. Now we will see what is managed heap ?
After the garbage collector is initialised, it allocates a segment of memory to keep and manage objects. This memory is known as the managed heap, rather than a native heap in the operating device.
There is a managed heap for each managed process. All threads within the process allocate memory for objects at the same heap.
The size of segments allotted with the aid of the garbage collector is implementation-specific and is problem to exchange at any time, inclusive of in periodic updates. Your app should in no way make assumptions about or rely on a specific section size, nor should it try and configure the quantity of memory available for segment allocations.
While a garbage collection is prompted, the garbage collector reclaims the memory that’s occupied by dead objects. The reclaiming process compacts live objects in order that they may be moved together, and the dead area is eliminated, thereby making the heap smaller. This guarantees that objects which might be allocated together stay together at the managed heap to hold their locality.
Generations in Garbage Collector
Garbage collection frequently takes place with the reclamation of short-lived objects. To optimize the performance of the garbage collector, the managed heap is divided into 3 generations, 0, 1, and 2, so it may take care of long-lived and quick-lived objects separately as shown in below image.
The garbage collector stores new objects in generation zero. objects created early in the application’s lifetime that live on collections are promoted and saved in generations 1 and 2. Because it’s faster to compact a portion of the managed heap than the entire heap, this scheme lets in the garbage collector to release the memory in a selected generation in place instead of release the memory for the complete managed heap on every occasion it performs a collection.
That is the youngest generation and carries short-lived objects. An instance of a brief-lived item is a temporary variable. Garbage collection happens most frequently on this generation.
Newly allocated objects shape a new generation of objects and are implicitly generation 0 collections. maximum objects are reclaimed for garbage collection in generation 0 and don’t continue to exist to the next generation.
If an application attempts to create a new item while generation zero is full, the garbage collector performs a collection in an attempt to free address area for the object. The garbage collector starts by examining the items in generation 0 in preference to all objects inside the managed heap. A collection of generation zero alone frequently reclaims enough memory to enable the application to hold creating new objects.
After the garbage collector plays a collection of generation 0, it compacts the memory for the available objects and promotes them to generation 1. Due to the fact objects that survive collections tend to have longer lifetimes, it makes feel to promote them to a better generation. The garbage collector would not have to re-examine the objects in generations 1 and a couple of every time it performs a collection of generation 0.
This generation contains long-lived objects. An example of a long-lived object is an object in a server application that contains static data that’s live for the duration of the process.
For maximum of the objects that your application creates, you can rely on garbage collection to routinely perform the vital memory management obligations. however, unmanaged resources require explicit clean-up. The most commonplace type of unmanaged resource is an object that wraps an operating system resource, including a file handle, window handle, or network connection. Even though the garbage collector is able to track the life of a managed item that encapsulates unmanaged resource, it would not have specific knowledge about how to clean up the resource.
When you create an object that encapsulates an unmanaged resource, it’s recommended that you provide the necessary code to clean up the unmanaged resource in a public Dispose method. By providing a Dispose method, you enable users of your object to explicitly free its memory when they are finished with the object. When you use an object that encapsulates an unmanaged resource, make sure to call Dispose as necessary.
Garbage collector manages the allocation and release of memory for your application. Each time you create a new object, runtime allocates memory for the object from the managed heap. As long as address space is available in the managed heap, the runtime continues to allocate space for new objects. However, memory is not infinite. Eventually the garbage collector must perform a collection in order to free some memory. The garbage collector’s optimizing engine determines the best time to perform a collection, based upon the allocations being made. When the garbage collector performs a collection, it checks for objects in the managed heap that are no longer being used by the application and performs the necessary operations to reclaim their memory.