.NET Basics – Garbage Collection – Part 1

.NET Basics – Garbage Collection – Part 1

Spread the love

It has been long time I have visited to Garbage Collection. I learnt on 10-12 years. I have been visiting again and this article I want to write about my learning. In nutshell, Garbage collector manages the allocation and releasing the memory of the application. It means for developer for managed code, they don’t have to write code to perform memory management tasks. It eliminate common problems, such forgetting to free an object and cousing memory leak.

Memory Fundamentals in CLR

  • Each process has its own separate virtual address space.
  • 32 bit computers, each process has 2gb user-mode virtual space
  • 64 bit computers, the virtual address spaces is 128TB
  • .NET developer works with virtual address space, never work with physical memory.
  • Objects are stored in Managed Heap.
  • virtual address space can get fragmented. It means there may be free blocks which are called holes in address space. When virtual memory allocation requested, it has to find single free block to fill that requirement.

Memory Allocation

When a new process starts, .NET runtime reserves a contiguous region of address space for the process, that is called managed heap. The managed heap maintains a pointer to the address where the next object in the heap will be allocated. When application create first reference type, memory allocated for the type at the base address of the managed heap. When application next object, it create following the first object and so on as long as memory space is available. Allocating memory from the managed heap is faster than unmanaged memory allocation. Because the runtime allocates memory for an object by adding a value to a pointer, it’s almost as fast as allocating memory from the stack.

Memory Release

Runtime determines what is the best time to run garbage collection. Once it start these the steps it performs:

  • Garbage Collector determine which object are not accessible by examining the applications root include
    • Static Fields
    • local variable on thread’s stack
    • CPU register
    • GC Handles and
    • Finalizer queue
  • Using above list garbage collector creates a object graphs with all objects which are accessible from application root.
  • Object which are not made to graph are considered unreachable object and releases the memory allocated to them.
  • During garbage collection when it examine the unreachable object it also copy the reachable object in released memory which called memory compact. It make memory contiguous.
  • Update the memory pointer so application’s root points to new location. I call this process memory defragmentation
  • To improve performance, Garbage collector doesn’t compact large object. Even large object are managed in separate heap called Large Object Heap.

When GC Runs

Garbage Collector runs in following scenarios:

  • System has low memory
  • The memory that’s used by allocated objects on the managed heap surpasses an acceptable threshold. This threshold is continuously adjusted as the process runs.
  • When application calls GC.Collect() Method.

Understating Generations

To improve the performance of garbage collection, Runtime categorized objects in managed heap in different generations. It happens due to following considerations:

  • Compacting memory of portion of managed heap is faster than entire managed heap.
  • Newer object consider for short timespan while older object may have longer timespan. Most of the time newer object used temporarily.

As mentioned above managed heap is divided into 3 generations 0,1 and 2. Newly created object always created in generation 0. If those objects are survived during Garbage collection, they are promoted to generation 1. If they survived during generation 1 garbage collection then they promoted to generation 2. And garbage collection in generation 2 rarely happens.

Garbage collection on generation 0 happens more frequently. When application create new object, it gets created. in generation 0 unless it large object like array. Large objects are created in large object managed heap. If application When application attempt to create new object when generation 0 is full, Garbage collection triggers. It examines generation 0 first and most of the large memory is released in this generation only.

If any object survived in generation 0 garbage collection, they are promoted to generation 1, assuming they are going to live for long time. Garbage collection on generation 1 happens only if generation 0 garbage collection could free up enough memory.

If any object survived during genration 1 garbage collection, then they are promoted to generation 2. Of course generation 2 garbage collection only happens if above two gc are not able to free up the space. Static variable are stored in this category.

Garbage Collection on generation 2 happens when system is running out of memory or user call GC.Collect() method in the code.

When the garbage collector detects that the survival rate is high in a generation, it increases the threshold of allocations for that generation. The next collection gets a substantial size of reclaimed memory. The CLR continually balances two priorities: not letting an application’s working set get too large by delaying garbage collection and not letting the garbage collection run too frequently.

Unmanaged Resources

Most of the time we work with managed object, but sometime we need to use object which is are not managed like file handle, Database connection, Windows handle. although Garbage collector able to determine lifetime of the object but it doesn’t know how to release memory for those object. In that case our code should be responsible for clearing those objects. Generally it is done by implementing IDisposable interface and explicitly by calling Dispose() mahod. It can also be done by implementing Object.Finalize() method which is destructor of the class. But it’ll not guarantee when your resource will be released or destroyed.