joydip_kanjilal
Contributor

My two cents on GC.Collect method in C#

opinion
Jul 22, 20164 mins
Software Development

Refrain from calling the GC.Collect method explicitly to reclaim the memory occupied by the objects in your application unless there is a specific reason do so

The GC.Collect() method has for long been popular among .Net developers. However, hardly few of us know how it actually works or, if a call to it is at all needed.

The CLR (Common Language Runtime) adopts garbage collection as a mechanism to clean up the resources consumed by your application. Note that when you create objects in .Net, they are stored in the managed heap, and when you are done using them, you need not worry about cleaning them — the runtime would do it for you.

The CLR organizes the managed heap into generations. The three generations into which the managed heap is organized are: Generation 0, Generation 1, and Generation 2. The GC is adept at reclaiming the memory occupied by managed objects. However, you should follow certain guidelines to facilitate faster garbage collection so as to improve your application’s performance.

Should I use the GC.Collect() method?

First off, do you at all need to call GC.Collect in your application’s code? The answer in most cases is no. Let me now tell you what this method does and why you should refrain from calling this method in most cases.

When you make a call to GC.Collect() method, the runtime performs a stack walk to determine the objects that are reachable and those that aren’t. It also freezes the main thread (and also any child threads it created) of the application. In other words, when the GC.Collect() method is called, the runtime performs a blocking garbage collection of all generations.

I would always prefer not to use GC.Collect() unless there is a specific reason to use it. A GC typically consists of the Mark and Sweep phases followed by a Compaction phase. The time spent by the runtime to perform a GC may become a bottleneck, so, use it only very rarely and if you really need to. Rico Mariani states: “Consider calling GC.Collect() if some non-recurring event has just happened and this event is highly likely to have caused a lot of old objects to die.”

Using the GC.Collect() method

Here’s how you can invoke the GC.Collect() method in your code.

GC.Collect();

Note that you can also collect objects that pertain to a specific generation.

GC.Collect() – used to collect objects present in the generations 0, 1, 2

GC.Collect(0) – used to collect objects present in generation 0

GC.Collect(1) – used to collect objects present in generations 0 and 1

You can also determine how much memory has been freed by making a call to the GC.Collect() method. To do this, you can take advantage of the System.GC.GetTotalMemory() method as shown in the code snippet below.

//Write code to create some large objects here

Console.WriteLine("Total available memory before collection: {0:N0}", System.GC.GetTotalMemory(false));

System.GC.Collect();

Console.WriteLine("Total available memory collection: {0:N0}", System.GC.GetTotalMemory(true));

The GC.GetGeneration() method can be used to know the generation to which an object belongs. Refer to the code listing given below.

static void Main(string[] args)

       {

           List<string> obj = new List<string>() { "Joydip", "Steve" };

           Console.WriteLine(System.GC.GetGeneration(obj));

           System.GC.Collect();

           Console.WriteLine(System.GC.GetGeneration(obj));

           System.GC.Collect();

           Console.WriteLine(System.GC.GetGeneration(obj));

           Console.Read();

       }

When you execute the above program, here’s what is printed in the console window.

0

1

2

As you can see, each call to the GC.Collect() method promotes the object “obj” to the next higher generation. This is because the object “obj” survives the garbage collection in each of the two cases, i.e., it is not reclaimed in any of the two calls made to the GC.Collect() method.

You can force garbage collection either to all the three generations or to a specific generation using the GC.Collect() method. The GC.Collect() method is overloaded — you can call it without any parameters or even by passing the generation number you would like to the garbage collector to collect.

Note that objects that have finalizers (and if a call to SuppressFinalize method hasn’t been made) would not be collected when a call to GC.Collect() method is made. Rather, such objects would be placed in the finalization queue. If you would like to collect those objects as well, you would need to make a call to the GC.WaitForPendingFinalizers() method so that those objects are cleaned up when the next GC cycle runs. In essence, reclaiming the memory occupied by objects that have finalizers implemented requires two passes since such objects are placed in the finalization queue rather than being reclaimed in the first pass when the garbage collector runs.

joydip_kanjilal
Contributor

Joydip Kanjilal is a Microsoft Most Valuable Professional (MVP) in ASP.NET, as well as a speaker and the author of several books and articles. He received the prestigious MVP award for 2007, 2008, 2009, 2010, 2011, and 2012.

He has more than 20 years of experience in IT, with more than 16 years in Microsoft .Net and related technologies. He has been selected as MSDN Featured Developer of the Fortnight (MSDN) and as Community Credit Winner several times.

He is the author of eight books and more than 500 articles. Many of his articles have been featured at Microsoft’s Official Site on ASP.Net.

He was a speaker at the Spark IT 2010 event and at the Dr. Dobb’s Conference 2014 in Bangalore. He has also worked as a judge for the Jolt Awards at Dr. Dobb's Journal. He is a regular speaker at the SSWUG Virtual Conference, which is held twice each year.

More from this author