In Dot Net
0
295

While developing an application, a developer creates many objects that occupy memory. An excess amount of unmanaged memory slows down the application performance. Traditional programming languages do not provide garbage collection features such as C language. Although C language ideally suits for embedded systems because of its low-level control portability and structured programming. So, working with such kind of programming languages end up with the much effort of freeing up memory manually. Dot net provides its native garbage collection feature and developer no longer has to explicitly free memory. Dot net garbage collector automatically releases the memory when a block of memory is no longer need in the program. This technique prevents memory leaks. In this article, I will explain how to collect garbage memory or objects in dot net c#. So let’s start.

What is a garbage collector and how does it work?

The garbage collector is one of the main features provided by CLR that helps us to clean unused managed objects. By cleaning unused managed objects it basically reclaims the memory. When a dot net application runs it can create several objects and at given moment of time. It is very much possible that it will not use some of those objects or it will not need some of those objects.

garbage collector

So basically for those objects, garbage collector runs as a background thread continuously at a specific interval. It reclaims the memory once it finds an unused object. So garbage collector is nothing but a background thread that runs continuously. Checks for unused managed objects clean those objects and reclaims the memory.

Now here one thing to notice is that garbage collector cleans and reclaim unused managed objects only, it does not clean unmanaged objects. So, in other words, anything which is outside the CLR boundary garbage collector will not clean the memory. There is one more concept in garbage collector that helps in collecting garbage that is generations. Basically, there are three generations that are generation 0, generation 1 and generation 2. So let’s try to understand what is the concept of generations and how does it affect garbage collector performance.

Generations in garbage collector :

garbage collector generation

Basically, there are three generations that are Generation 0, Generation 1 and Generation 2 let’s talk about its key features.

  • Generation 0: This is the youngest generation. All the objects will keep in this generation when an application will create objects. Basically, this generation contains short-lived objects and object collection is very frequent in this generation. Objects that survive this generation promotes to the next generation that is Generation ‘1’. 
  • Generation 1: This generation contains objects those were not claim in Generation 0. Garbage collector clears most of the memories and objects in Generation 0 itself but few still having references moves to the next generation.
  •  Generation 2: This generation contains those objects those were not claim in Generation 1 and promoted to this generation. Basically, this generation contains long lived objects

Let’s understand this with below given example.

garbage collector generation example

In the above example, you can see when the application executes then all the initialized variables are in generation zero. When periodically garbage collector finds an object that is not referenced in the program anymore then it cleans that object and reclaims its memory and promotes rest of the object to next generation but remember at the same time it checks for unused objects in generation one and generation two also but at this point in time it will not find objects in both the generations.

Again in the next periodic cycle, it searches for unused memory in generation zero and reclaims if finds any unused objects and promotes to the next generation. Similarly, this process is repeated for generation one also and seeks for unused objects and reclaims its memory and promotes to next generation.

How to collect garbage objects

You don’t need to do anything If your application creates managed objects. You will have to collect garbage objects explicitly if your application references unmanaged objects by using their native file handles. There are two methods for releasing unmanaged resources Dispose and Finalize.

Dispose() method

This is just like any other methods in the class and can be called explicitly but the main role of this method is to clean up the objects. In the dispose method we write clean up code for the object. It is important that we free up all the unmanaged resources in the dispose method like database connection, files etc.

Finalize() method

Finalize () is called by Garbage Collector implicitly to free unmanaged resources. The garbage collector calls this method at some point after there are no longer valid references to the object. You must implement this when you are writing a custom class that will be used by other users.

Code example-


using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
public abstract class Base : IDisposable
{
private bool disposed = false;
private string instanceName;
private List<object> trackingList;
public Base(string instanceName, List<object> tracking)
{
this.instanceName = instanceName;
trackingList = tracking;
trackingList.Add(this);
}
public string InstanceName
{
get
{
return instanceName;
}
}
//Implementing IDisposable.
public void Dispose()
{
Console.WriteLine("\n[{0}].Base.Dispose()", instanceName);
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// Freeing other (managed objects).
Console.WriteLine("[{0}].Base.Dispose(true)", instanceName);
trackingList.Remove(this);
Console.WriteLine("[{0}] Removed from tracking list: {1:x16}",
instanceName, this.GetHashCode());
}
else
{
Console.WriteLine("[{0}].Base.Dispose(false)", instanceName);
}
disposed = true;
}
}
// Using C# destructor syntax for finalization code.
~Base()
{
Console.WriteLine("\n[{0}].Base.Finalize()", instanceName);
Dispose(false);
}
}
public class Derived : Base
{
private bool disposed = false;
private IntPtr umResource;
public Derived(string instanceName, List<object> tracking) :base(instanceName, tracking)
{
// Saving the instance names as an unmanaged resources
umResource = Marshal.StringToCoTaskMemAuto(instanceName);
}
protected override void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
Console.WriteLine("[{0}].Derived.Dispose(true)", InstanceName);
// Release managed resources.
}
else
{
Console.WriteLine("[{0}].Derived.Dispose(false)", InstanceName);
}
// Release unmanaged resources.
if (umResource != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(umResource);
Console.WriteLine("[{0}] Unmanaged memory freed at {1:x16}",
InstanceName, umResource.ToInt64());
umResource = IntPtr.Zero;
}
disposed = true;
}
// Calling Dispose in the base class.
base.Dispose(disposing);
}
}
public class TestDisposal
{
public static void Main()
{
List<object> tracking = new List<object>();
// Dispose is not yet called, Finalize method will be called later on.
using (null)
{
Console.WriteLine("\nDisposal Scenario: #1\n");
Derived d3 = new Derived("d1", tracking);
}
// Dispose method is implicitly called in the scope of the using statement.
using (Derived d1 = new Derived("d2", tracking))
{
Console.WriteLine("\nDisposal Scenario: #2\n");
}
// Dispose method is explicitly called.
using (null)
{
Console.WriteLine("\nDisposal Scenario: #3\n");
Derived d2 = new Derived("d3", tracking);
d2.Dispose();
}
// Again, Dispose method is not called yet, Finalize will be called later on.
using (null)
{
Console.WriteLine("\nDisposal Scenario: #4\n");
Derived d4 = new Derived("d4", tracking);
}
// Object still remaining to dispose.
Console.WriteLine("\nObjects remaining to dispose = {0:d}", tracking.Count);
foreach (Derived dd in tracking)
{
Console.WriteLine("    Reference Object: {0:s}, {1:x16}",
dd.InstanceName, dd.GetHashCode());
}
// Queued finalizers will be executed when Main() goes out of scope.
Console.WriteLine("\nDequeueing finalizers...");
}
}

Concluding words

I hope this article helped you to understand about Garbage collection, its advantages and how the garbage collector works. In my next blog, I will describe more about garbage collector and difference between Dispose and Finalize.

Recommended Posts

Start typing and press Enter to search