Pages

Thursday, August 12, 2010

Garbage Collection Notifications in .NET 4.0

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 :

  1. Always leave local variable.
  2. Set class variables, events etc to null. 
This leaves us with lots of queries. When does the garbage collection executes? 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 came to .NET environment. As for me too, I was doing the application just blindly taking it 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 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 reclaim So, there is still uncertainty whether actually the Garbage Collection will 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 phase of GC collection, actually the memory is not been 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.

Whats new in .NET 4.0 (or .NET 3.5 SP1)
 Well, GC is changed a bit with the introduction of .NET 4.0, so that the programmer have better flexibility on how to use GC. One of such changes is the signature of GC.Collect()



Initially, GC.Collect only allows 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 specified generation to collect all generation 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 its 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 to run smoothly. So how can you do this ?  Lets look at the steps :

  1. Call GC.RegisterForFullGCNotification to allow for notifications when GC is approaching.
  2.  Create a new thread from the application and start poll continuously in an infinite loop to methods GC.WaitForFullGCApproach and/or GC.WaitForFullGCComplete methods. 
  3. Both the method returns GCNotificationStatus.Succeeded when the notification has to be raised. 
  4. In Calling thread use GC.CancelFullGCNorification to unregister the notification process. 
Now lets implement the code :

public class MainProgram
{
    public static List<char[]> lst = new List<char[]>();
    public static void Main(string[] args)
    {
        try
        {
            // Register for a notification. 
            GC.RegisterForFullGCNotification(10, 10);

            // Start a thread using WaitForFullGCProc.
            Thread startpolling = new Thread(() =>
            {
                while (true)
                {
                    // Check for a notification of an approaching collection.
                    GCNotificationStatus s = GC.WaitForFullGCApproach(1000);
                    if (s == GCNotificationStatus.Succeeded)
                    {
                        //Call event

                        Console.WriteLine("GC is about to begin");
                        GC.Collect();

                    }
                    else if (s == GCNotificationStatus.Canceled)
                    {
                        // Cancelled the Registration
                    }
                    else if (s == GCNotificationStatus.Timeout)
                    {
                        // Timeout occurred.
                    }

                    // Check for a notification of a completed collection.
                    s = GC.WaitForFullGCComplete(1000);
                    if (s == GCNotificationStatus.Succeeded)
                    {
                        //Call event
                        Console.WriteLine("GC has ended");
                    }
                    else if (s == GCNotificationStatus.Canceled)
                    {
                        //Cancelled the registration
                    }
                    else if (s == GCNotificationStatus.Timeout)
                    {
                        // Timeout occurred
                    }

                    Thread.Sleep(500);
                }

                    
            });
            startpolling.Start();

            //Allocate huge memory to apply pressure on GC
            AllocateMemory();
                
            // Unregister the process
            GC.CancelFullGCNotification();

        }
        catch { }
    }

    private static void AllocateMemory()
    {
        while (true)
        {
                
            char[] bbb = new char[1000]; // creates a block of 1000 characters
            lst.Add(bbb);                // Adding to list ensures that the object doesnt gets out of scope   
            int counter = GC.CollectionCount(2);
            Console.WriteLine("GC Collected {0} objects", counter);

        }
    }
        
}

Here when the program starts I have called RegisterForFullGCNotification. This call makes 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 applicaition 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 polling 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 Notiification.

From the main thread, we continuously allocating memory to check when GC collection occurs. The GC.CollectionCount to determine how many GC collection took place at a given instant of time. Use 0 - 2 to check each generation individually.


Download Sample application - 30KB


Please feel free to leave your comment on this new feature.

No comments:

Post a Comment

Please make sure that the question you ask is somehow related to the post you choose. Otherwise you post your general question in Forum section.