C# GC.Collect Examples: CollectionCount, GetTotalMemory

This C# example program demonstrates the GC.Collect method. It forces a garbage collection.
GC.Collect. The C# language is a garbage-collected language. This means that memory that is no longer referenced by your program will be reclaimed and is later reused. With GC.Collect, we force a garbage collection to occur at any time.
Example. First, this example invokes the GC.Collect method. Three calls to get the total memory usage on the system are present. They occur before the allocation, after the allocation, and after the forced garbage collection.

Tip: You can see that memory returns to its low level after the garbage collection.

C# program that uses GC.Collect using System; class Program { static void Main() { long mem1 = GC.GetTotalMemory(false); { // Allocate an array and make it unreachable. int[] values = new int[50000]; values = null; } long mem2 = GC.GetTotalMemory(false); { // Collect garbage. GC.Collect(); } long mem3 = GC.GetTotalMemory(false); { Console.WriteLine(mem1); Console.WriteLine(mem2); Console.WriteLine(mem3); } } } Output 45664 245696 33244
Discussion. So when should you call the GC.Collect method in your .NET programs? In my experience, you should never call it. The call will usually not do much to reduce overall memory usage. And it will impose a slowdown whenever it is called.

In ASP.NET: The GC.Collect on one application will cause a performance hit on all web sites in the same pool.

This method adds more complexity to your program and probably even reduces overall performance. The .NET garbage collector is finely tuned by Microsoft. They have more time and money to spend on tweaking its performance than you do.

And: I have used GC.Collect (without success) in attempts to improve performance, particularly after a lot of allocations after startup.

CollectionCount. GC.CollectionCount returns an int. It tells us how many times garbage collection has occurred. Getting information about how often garbage collection is occurring in your program is not always simple.

Tip: With CollectionCount and the GC.MaxGeneration property, we get this information.

Note: Because most objects die young, generations are used to optimize which elements are scanned.

And: Newer objects are scanned more often because they are most likely to have become dead.

C# program that uses CollectionCount using System; class Program { static void Main() { // This loop does a lot of allocations! for (int i = 0; i < 100; i++) { for (int a = 0; a < 1000; a++) { System.IO.Path.GetRandomFileName(); System.IO.Path.GetRandomFileName(); } System.Threading.Thread.Sleep(1); } // Display collection counts. for (int i = 0; i <= GC.MaxGeneration; i++) { int count = GC.CollectionCount(i); Console.WriteLine(count); } } } Output 15 0 0
Program output. On my computer, the program resulted in 15 generation 0 garbage collections. This is because the memory allocations caused by GetRandomFileName were all put in generation 0.

Then: They died, meaning they lost any reference from the program's flow graph. As is sadly the typical case, these objects died young.

GetTotalMemory. GC.GetTotalMemory returns the number of bytes allocated. It accesses statistics about the managed heap. It returns data about the entire .NET Framework, not the specific program. With it we determine the sizes of objects in memory.

Parameter: The method receives one parameter of type bool, which lets you demand a garbage collection to occur before taking the numbers.


Next: This program shows the memory difference after allocating ten million bytes and then collecting them.

Info: We pass a bool to GC.GetTotalMemory. False indicates that an expensive collection should not be forced.


Usually: Forcing a GC has no benefit in programs. It just adds to the complexity. It is best to leave this parameter as false.

C# program that uses GC using System; class Program { static void Main() { long bytes1 = GC.GetTotalMemory(false); // Get memory in bytes byte[] memory = new byte[1000 * 1000 * 10]; // Ten million bytes memory[0] = 1; // Set memory (prevent allocation from being optimized out) long bytes2 = GC.GetTotalMemory(false); // Get memory long bytes3 = GC.GetTotalMemory(true); // Get memory Console.WriteLine(bytes1); Console.WriteLine(bytes2); Console.WriteLine(bytes2 - bytes1); // Write difference Console.WriteLine(bytes3); Console.WriteLine(bytes3 - bytes2); // Write difference Console.ReadLine(); } } Output 21060 Program started with these bytes. 10021092 After ten million bytes allocated. 10000032 Difference. 12860 After garbage collection. -10008232 Difference.
Managed heaps. The GC.GetTotalMemory method operates on the concept of the managed heap, not the program that you are calling it from. Programs that allocate data on the managed heap are called mutators. One managed heap can have many mutators.

Note: When you invoke GC.GetTotalMemory, you will be counting the allocations from all the mutator programs.

And: On a web server, this would add allocations from other programs also running in ASP.NET.

Statistics. There is an excellent way of getting a general feel of the amount of memory your program is allocating on the managed heap. You can set up a special web page or dialog in your program that reports the statistics from GetTotalMemory.
It can store the previous figure in a static field, and then subtract the second from the first to see the change. This does not give you precise and exact figures. But it helps you determine if memory is or will become a problem.
Summary. The GC.Collect method is best used for diagnostics purposes (like determining a base line of memory usage). You can also use it to see if all the objects you no longer need are not reachable and can be collected by the garbage collector.

However: In deployment, the GC.Collect method is not useful because it often has little benefit and might even reduce performance.

© 2007-2019 Sam Allen. Every person is special and unique. Send bug reports to
Dot Net Perls