Garbage Collection - Dispose Vs Finalize And IDisposable Pattern

Introduction

In this article, we are going to see how to Finalize, Destructor, IDisposable pattern, and Use block work in a C# application.

Let's have a small introduction to all of them.

Finalize - This is used to release unmanaged resources.

The managed resources are like classes created in C#, Vb.Net, and any other class created under .net languages.

The cleanup and releasing of the unmanaged resources are done under the finalize method.

If your class is not using unmanaged resources then forget about the Finalize method. If your class is using unmanaged resources then implement finalize method.

We can not call the finalize method using the class object. The finalized method is called by garbage collector.

Finalize queue

The first time when the object is created then it is going to sit in generation 0. The Garbage collector frequently checks generation 0 whether any object is inactive. If the object is inactive and no one is using the object then immediately it is going to release and deallocate the memory.

In the same way, the garbage collector checks the object header and finds the object has implemented the finalize method then the garbage collector moves that object to the finalize queue.

The checking of the finalized queue is much less. Even if the object is inactive and the object is not more in usage still it will be in the memory heap and also we don’t know when it will release the unmanaged resources.

When the code is compiled then the destructor is converted to finalize. Whenever we use unmanaged resources then make sure to implement the destructor method.

Create a sample console project.

Add classes as below

namespace GarbageCollectorProgram
{
    class ClassA
    {
        public ClassA()
        {
            Console.WriteLine("Created ClassA");
        }
        ~ClassA()
        {
            Console.WriteLine("Destructing -- ClassA");
        }
    }
    class ClassB : ClassA
    {
        public ClassB()
        {
            Console.WriteLine("Creating ClassB");
        }
        ~ClassB()
        {
            Console.WriteLine("Destructing -- ClassB");
        }
    }
    class ClassC : ClassB
    {
        public ClassC()
        {
            Console.WriteLine("Creating ClassC");
        }
        ~ClassC()
        {
            Console.WriteLine("Destructing -- ClassC");
        }
    }
    class ClassD
    {
    }
}

Open program.cs

namespace GarbageCollectorProgram
{
    class Program
    {
        static void Main(string[] args)
        {
            ClassC objC = new ClassC();
            Console.WriteLine("Object C is created..");
            objC = null;
            Console.WriteLine("Object C is assigned to null.. Object now destructing..");
            Console.ReadLine();
        }
    }
}

Class

You can see the finalized method, but where is the destructor method? This is getting converted to override the finalize method. So, using the destructor we define the finalize method.

Let us see how the destructor is calling.

Run the application

Created class

You can see the garbage collector didn’t call the finalize method of these classes.

Then when we close the exe at that time it will call the finalize method of all the classes. Even if the object is inactive it is still a Garbage collector who maintains the object’s memory.

Final method of all classes

GC.Collect();  

Let’s see what GC.Collect() does.

The garbage collector’s Collect() method instructs the GC to clear off or deallocate all the inactive object’s memory. This is a very expensive operation and goes to all the generations (0,1,2) to clear off the memory or deallocate the objects in one shot.

In our program objC = null; we are doing nothing but making the object objC inactive and the statement GC.Collect() goes on and immediately releases the objects.

Change the main method’s statements as below.

ClassC objC = new ClassC();  
            Console.WriteLine("Object C is created..");  
            objC = null;  
            GC.Collect();  
            Console.WriteLine("Object C is assigned to null.. Object now destructing..");  
            Console.ReadLine();  

Now run the application

Run the application

There is a performance issue when keeping the inactive objects memory for a long time and this is not the right technique to manage the memory,

IDisposable Interface

IDisposable has only one method which is the Dispose method. We can use the disposal method for the same purpose to clean up the unmanaged resources. So when you are done with your object then simply call the dispose method.

Let us take an example that will implement the Dispose method of the IDisposable interface.

Add the class below

namespace GarbageCollectorPorgram
{
    public class DisposableTest : IDisposable
    {
        private bool isDisposed = false;

        public void Print(string message)
        {
            Console.WriteLine("Hello " + message);
        }
        ~DisposableTest()
        {
            Console.WriteLine("Destructor/Finalize of DisposableTest");
            Dispose(false);
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        protected void Dispose(bool dispose)
        {
            if (!isDisposed)
            {
                if (dispose)
                {
                    // to cleanup managed objects
                }
                // To cleanup unmanaged resources/objects
                isDisposed = true;
            }
        }
    }
}

Click on IDisposable and press f12

Namespace

You can see there is only one method Dispose.

If we are implementing the Dispose method then it means we have got the control of whenever you want to call the dispose method based on the right time according to you. So whoever is using the class has the responsibility to clear the unmanaged resources.

If you are depending on the disposal method then there is a possibility that we forgot to call the disposal method which will happen Also, it may cause a memory leak.

So we have implemented the finalize as well as the Dispose method. Now, if the user forgets to call the disposal method then that time my finalized method will activate. Also if the user calls the dispose method then I don’t want to call the finalize method to activate. So we have specified in Dispose method().

public void Dispose()
{
    Dispose(true);
    GC.SuppressFinalize(this);
}

Suppose the user called the dispose method then internally we are specifying the GC to suppress the finalize for the particular 'this' object. That means it will be taken out from the finalize queue immediately, and the Finalize method will not be triggered by the Garbage collector.

Also, we can use IDisposable for releasing the managed, unmanaged resources and components.

In our program, we have finalized the method to release unmanaged resources.

~DisposableTest()
{
    Console.WriteLine("Destructor/Finalize of DisposableTest");
    Dispose(false);
}

We are passing false inside the Dispose method.

protected void Dispose(bool dispose)
{
    if (!isDisposed) // will be true
    {
        if (dispose)
        {
            // to cleanup managed objects
        }
        // To cleanup unmanaged resources/objects
        isDisposed = true;
    }
}

So, the first step will be true and it will check another if(dispose) that will be false, and this will only clean up the unmanaged resources and set the value idDisposed to true.

Open program.cs

namespace GarbageCollectorPorgram
{
    class Program
    {
        static void Main(string[] args)
        {
            DisposableTest obj = new DisposableTest();
            Console.WriteLine("DisposableTest object is created..");
            obj.Print("Good Morning..");
            obj = null;
            Console.WriteLine("Assigned null..  Object is destructing..");
            Console.ReadLine();
        }
    }
}

Run the application

Dispose

The finalize method has not been called.

Press any key

Destructor

Then the finalize method is called.

So when we close the exe the garbage collector calls the finalize method. Even if you have implemented the IDisposable dispose method but forgot to call the dispose method then it will call the finalize method.

Now let us call the disposal method

Change the program.cs statements

namespace GarbageCollectorPorgram
{
    class Program
    {
        static void Main(string[] args)
        {
            DisposableTest obj = new DisposableTest();
            Console.WriteLine("DisposableTest object is created..");
            obj.Print("Good Morning..");
            obj.Dispose();
            obj = null;
            Console.WriteLine("Assigned null..  Object is destructing..");
            Console.ReadLine();
        }
    }
}

Run the application

Assigned null

Now you will not see the Destructor/finalize of the object statement execution. It means the Dispose is going to take care of releasing the unmanaged resources and also suppress the finalize method.

Using block

Open program. cs and change the statements as below.

namespace GarbageCollectorPorgram
{
    class Program
    {
        static void Main(string[] args)
        {
            using (DisposableTest obj = new DisposableTest())
            {
                Console.WriteLine("DisposableTest object is created..");
                obj.Print("Good Morning..");
            }
            Console.WriteLine("Assigned null..  Object is destructing..");
            Console.ReadLine();
        }
    }
}

To avoid the calling of dispose method and to forget the calling of dispose method. Dot Net has introduced a Usign block.

So if you are creating the object that has implemented the IDisposable interface then Using block will take care of calling the dispose method for the object. At the end of the block, it will automatically call the Dispose method.

Run the application

Object destruct

You can see the object didn’t call the finalize method.

So if any class has implemented the IDisposable interface then try using the Using block to create the instance.

So many classes like the db connection class have implemented the Dispose method. So, if you are seeing the Dispose method has been implemented then try using the Using block to create the object.

EG. If you are creating the SqlConnection object/instance then try creating under Using block because SqlConnection implements the Dispose method that dispose method is used to release the DB Connection, and db Connection are unmanaged resources.

So the best practice is that if you are using the class/.net classes that have implemented the Dispose method then try to create an object under Using block. If you are not using the Using block then make sure to call the dispose method explicitly. This practice will help to avoid the GC finalize queue, and also to improve the performance.

Thank you.


Similar Articles