Demystify Garbage Collection in C#: Part 1

Hi friends, I hope all of you are fine. In this series of articles we will try to learn a few general concepts of garbage collection in C#. In a future article we will dig in dipper and try to learn a few behind-the-scenes concepts.

In the context of garbage collection (I hope you are familiar with the term already and are familiar with the basics of it) in C# is the subject of .NET support of automatic garbage collection. So the conclusion is "Being a .NET developer we should not be concered with memory management and the freeing of memory properly allocated by an object". And here the misconceptions begin.

Friend, I am not accepting that .NET does not support automatic garbage collection, it does support it and GC.collect() exists. And with due respect to GC.collect() I am making the statement "Don't call/use GC.Collect() here and there and frequently". Please don't ask me why right now. To provide the answer I need to introduce another article, in a future article we will talk about it. For now I will provide a one-line answer. GC.collect() collects all unused objects from all generations and it's a very resource-intensive process.

OK, let's return to the topic of garbage collection. If you are from a C/C++ background then you might be aware of destructors used to free resources created by a constructor (as we read from the book).
Let me tell you my little experience. One day in a language practical lab in our college there was a practical session of Destructors. A friend sitting beside me asked me, "Hey Sourav, Look at my program. I have defined a destructor but it's not executing". I checked his program (yes, he has defined a destructor with proper syntax) and found he did not a create scope within the main when he is creating the object. As we know, the key concept of a destructor in C++ is "when the object goes out of scope and is no longer in use, destructors run and clear the resources".

But the same concept is not true in C#. Let's check whether it works or not. In the following I have written a small code snippet to do the same and have created an object within the scope in the Main() function.

using System;

using System.Collections;

using System.Globalization;

using System.Data.SqlClient;

using System.Data;

 

namespace Test1

{

   public class Garbage

   {

          public Garbage()

          {

            Console.WriteLine("Reserve memory");

          }

          ~Garbage()

          {

              Console.WriteLine("Free memory");

          }

 

   }

    class Program

    {

        static void Main(string[] args)

        {

            {   //Genearte scope here

                Garbage g = new Garbage();

                g = null;

            }

            Console.ReadLine();

        }

    }

}

GarbageCollection.jpg

Here we are clearly seeing, the destructor is not running in spite of creating scope. From here the concept of garbage collection begins. In C#, the destructor does not run when the object goes out of scope.

Then, how we will free memory when we finish work with object?

The question is very good and the very bad solution is to use GC.Collect(). If you are reading from the first line then I think already you know why the solution is bad. Any the way to clear resources when we are done with an object using GC.collect(). Have a look at the following code:
 

using System;

using System.Collections;

using System.Globalization;

using System.Data.SqlClient;

using System.Data;

 

namespace Test1

{

   public class Garbage

   {

          public Garbage()

          {

            Console.WriteLine("Reserve memory");

          }

          ~Garbage()

          {

              Console.WriteLine("Free memory");

          }

 

   }

    class Program

    {

        static void Main(string[] args)

        {

            {

                Garbage g = new Garbage();

                g = null;

                GC.Collect();

            }

            Console.ReadLine();

        }

    }

}

GarbageCollection1.jpg

Now , we can see that the destructor is being executed. But one thing we need to keep in mind. The Garbage Collector can collect those objects that are null and no longer referenced anywhere. To understand the concept in practice, have a look at the following code.

Garbage Collector can collect only unused object

Let's prove this concept here. In the following example we will try to collect an object that is not free:
 

using System;

using System.Collections;

using System.Globalization;

using System.Data.SqlClient;

using System.Data;

 

namespace Test1

{

   public class Garbage

   {

          public string Name = null;

          public Garbage()

          {

            Console.WriteLine("Reserve memory");

          }

          ~Garbage()

          {

              Console.WriteLine("Free memory");

          }

 

   }

    class Program

    {

        static void Main(string[] args)

        {

            {

                Garbage g = new Garbage();

                g.Name = "sourav Kayal";

                GC.Collect();

            }

            Console.ReadLine();

        }

    }

}


Here is the output:

GarbageCollection2.jpg

In the output screen we are seeing the object is not collected by GC.collect() because it is not unused or in other sense , it is not garbage.

Then, how we will make garbage? Or clear reference of any object?

It's very simple. Just assign a null when you are done with the object. Say, for example, you are using one object and you are finished working with it. Then simply assign null to it. The advantage is that when GC runs in the background the object will be treated as garbage and it will get collected by the Garbage Collector. In the following code, after using the object we are assigning a null to it. Then we are running GC.collect() to collect the garbage.
 

using System;

using System.Collections;

using System.Globalization;

using System.Data.SqlClient;

using System.Data;

 

namespace Test1

{

   public class Garbage

   {

          public string Name = null;

          public Garbage()

          {

            Console.WriteLine("Reserve memory");

          }

          ~Garbage()

          {

              Console.WriteLine("Free memory");

          }

 

   }

    class Program

    {

        static void Main(string[] args)

        {

            {

                Garbage g = new Garbage();

                g.Name = "sourav Kayal";

                g = null; //Free object by setting null

                GC.Collect();

            }

            Console.ReadLine();

        }

    }

}


Here is output:

GarbageCollection3.jpg

Let's clear up the concept of generation

There is the concept of generation in garbage collection. When a new object is created it is created in generation 0. And when there is a shortage of memory, the Garbage Collector runs in the background and
collects all unused objects from memory. Now, when GC is unable to collect any object from generation 0 and if it finds that there is a destructor defined for that object , the object is moved to generation 1. In the following example we will see how to check the generation of a specified object.
 

using System;

using System.Collections;

using System.Globalization;

using System.Data.SqlClient;

using System.Data;

 

namespace Test1

{

   public class Garbage

   {

          public string Name = null;

          public Garbage()

          {

            Console.WriteLine("Reserve memory");

          }

          ~Garbage()

          {

              Console.WriteLine("Free memory");

          }

 

   }

    class Program

    {

        static void Main(string[] args)

        {

            {

                Garbage g = new Garbage();

                g.Name = "sourav Kayal";

                Console.WriteLine("Generation of object:- "GC.GetGeneration(g));

            }

            Console.ReadLine();

        }

    }

}

GarbageCollection4.jpg

Here it is showing that the object was created in Generation 0. Now in this situation if we try to collect the object of garbage class(g) by GC.Collect() then it will not be collected because a reference of the object still exists in the program. So that GC.collect() is unable to collect that object.

Where the object will be moved to

The object will move to the generation 1 object pool. Let's prove it with an example.
 

using System;

using System.Collections;

using System.Globalization;

using System.Data.SqlClient;

using System.Data;

 

namespace Test1

{

   public class Garbage

   {

          public string Name = null;

          public Garbage()

          {

            Console.WriteLine("Reserve memory");

          }

          ~Garbage()

          {

              Console.WriteLine("Free memory");

          }

 

   }

    class Program

    {

        static void Main(string[] args)

        {

            {

                Garbage g = new Garbage();

                g.Name = "sourav Kayal";

               

                Console.WriteLine("Generation of object:- " + GC.GetGeneration(g));

                GC.Collect();

                Console.WriteLine("Generation of object:- "GC.GetGeneration(g));

               

            }

            Console.ReadLine();

        }

    }

}

GarbageCollection5.jpg

Conclusion

Those are the fundamental concepts of garbage collection in C#. I hope you enjoy it and in a few future articles we will play more with GC.


Similar Articles