Memory management is primary for any application. From the very beginning, we have used destructors, or deleted the allocated memory whilst using the other programming languages like C or C++. C# on the other hand being a proprietor of .NET Framework provides us a new feature so that the programmer does not have to bother about the memory deallocation and the framework does it automatically. The basic usage is:
- Always leave local variable
- Set class variables, events, etc. to
null
This leaves us with lots of queries. When does the garbage collection execute? How does it affect the current application? Can I invoke Garbage collection when my system / application is idle?
These questions might come to any developer who has just come to the .NET environment. As for me too, I was doing the application just blindly taking into account that this might be the basic usage of .NET applications, and there might be a hidden hand for me who works for me in background. Until after a few days, I got an alternative to call the Garbage collection using GC.Collect()
.
But according to the documentation, GC.Collect()
is a request. It is not guaranteed that the Collection process will start after calling the method and the memory is reclaimed. So, there is still uncertainty whether the Garbage Collection will actually occur or not.
Why GC.Collect is Discouraged?
GC.Collect
actually forces the Garbage collection to invoke its collection process out of its regular cycle. It potentially decreases the performance of the application which calls it. As GC.Collect
runs in the thread on which it is called, it starts and quickly finishes the call. In such a phase of GC collection, actually the memory is not being reclaimed, rather it just produces a thorough scan on objects that are out of scope. The memory ultimately freed whenever Full Garbage collection takes place.
For reference, you can read Scotts' blog entry.
What's new in .NET 4.0 (or .NET 3.5 SP1)?
Well, GC has changed a bit with the introduction of .NET 4.0, so that the programmer has better flexibility on how to use GC. One of such changes is the signature of GC.Collect()
.
Initially, GC.Collect
only allowed you to use Optimization call. Now it has 3 overloads:
- GC.Collect (): Forces an immediate garbage collection for all generations
- GC.Collect(int): You pass the Generation 0 to a specified generation to collect all generations starting from 0.
- GC.Collect(int, GCCollectionMode): You can also specify the
CollectionMode
in addition to it. The CollectionMode
can be CollectionMode.Force
, CollectionMode.Optimized
, and Default
which is Forced
.
GC.Collect
is Forced
by default, so once you call it, it will automatically invoke the collection process.
So it's a great relief to all of you, right?
GC Notifications
Another feature that is introduced with GC in .NET 3.5 SP 1 is to produce notifications whenever GC collection is about to begin and GC collection is completed successfully. Say you are between a very resource consuming phase of your application, GC notification will allow you to get notified that GC is approaching, so that you could stop the current process and wait for GC to complete. This makes your application run smoothly. So how can you do this? Let's look at the steps:
- Call
GC.RegisterForFullGCNotification
to allow for notifications when GC is approaching. - Create a new thread from the application and start poll continuously in an infinite loop to methods
GC.WaitForFullGCApproach
and/or GC.WaitForFullGCComplete
methods. - Both the method return
GCNotificationStatus.Succeeded
when the notification has to be raised. - In
Calling
thread, use GC.CancelFullGCNorification
to unregister the notification process.
Now let's implement the code:
public class MainProgram
{
public static List<char[]> lst = new List<char[]>();
public static void Main(string[] args)
{
try
{
GC.RegisterForFullGCNotification(10, 10);
Thread startpolling = new Thread(() =>
{
while (true)
{
GCNotificationStatus s = GC.WaitForFullGCApproach(1000);
if (s == GCNotificationStatus.Succeeded)
{
Console.WriteLine("GC is about to begin");
GC.Collect();
}
else if (s == GCNotificationStatus.Canceled)
{
}
else if (s == GCNotificationStatus.Timeout)
{
}
s = GC.WaitForFullGCComplete(1000);
if (s == GCNotificationStatus.Succeeded)
{
Console.WriteLine("GC has ended");
}
else if (s == GCNotificationStatus.Canceled)
{
}
else if (s == GCNotificationStatus.Timeout)
{
}
Thread.Sleep(500);
}
});
startpolling.Start();
AllocateMemory();
GC.CancelFullGCNotification();
}
catch { }
}
private static void AllocateMemory()
{
while (true)
{
char[] bbb = new char[1000]; lst.Add(bbb); int counter = GC.CollectionCount(2);
Console.WriteLine("GC Collected {0} objects", counter);
}
}
}
Here when the program starts, I have called RegisterForFullGCNotification
. This call registers the GC to start notifying using the notifiers. Please note that the call to RegisterForFullGCNotification
will fail if a concurrent GC is enabled. You will see "This API is not available when the concurrent GC is enabled." Concurrent GC means the garbage collection will run in separate threads concurrently. If your application has to do some heavy task, leave concurrent GC enabled. This makes the GC run collection without interrupting the application thread. For ASP.NET application, you should disable concurrentGC
.
To disable Concurrent
GC, add App.config and put:
<configuration>
<runtime>
<gcConcurrent enabled="false" />
</runtime>
</configuration>

After concurrent GC is disabled, you can run the application. After calling RegisterForFullGCNotification
, we start a new thread. In the thread, we continuously poll WaitForFullGCApproach
which returns GCNotificationStatus.Succeeded
whenever the GC is approaching. It is better to call GC.Collect
in this situation to ensure that collection starts immediately.
We also poll WaitForFullGCComplete
which returns GCNotificationStatus.Succeeded
whenever the GC collection is complete.
We use GC.CancelFullGCNotification()
to stop using Notification.
From the main thread, we are continuously allocating memory to check when GC collection occurs. The GC.CollectionCount
to determine how much GC collection took place at a given instance of time. Use 0 - 2 to check each generation individually.
Download sample application - 30 KB
Please feel free to leave your comments on this new feature.