Garbage Collection Notifications in .NET 3.5 SP1

August 25, 2008


One (or three) of the first questions native developers ask when learning about the .NET garbage collector is this:

How do I control the garbage collector?  How do I get a notification when a garbage collection occurs?  What do I do to limit the garbage collector’s impact on my application’s performance?

I would be shooting myself in the foot by saying that these questions should not be addressed with the seriousness they deserve.  The garbage collector is one of the primary factors affecting the performance of managed applications.  It is difficult to control and predict its behavior.  There is a variety of native (CLR hosting) and managed APIs for obtaining information from and controlling the GC’s heuristics, including the GC flavor, segment sizes, generation startup limits, collection notifications and allocation control.

As of .NET 3.5 SP1, another set of APIs joins the existing interfaces.  In .NET 3.5 SP1, it is possible to receive a notification when a garbage collector is approaching and act accordingly if application needs dictate it.  (These APIs are available only if concurrent GC is disabled – i.e. when using workstation non-concurrent GC or server GC.)

The basic model is as follows.  If an application is very sensitive to garbage collection timing, it might want to receive a notification when GC is about to occur, and receive yet another notification when GC has completed.  The MSDN example cites a server application processing low-latency requests across multiple server instances.  If a full GC is imminent, the application might want to redirect requests to another server instance while the collection is taking place.

This scenario is plagued with caveats.  Redirecting load to another server instance might take longer than the GC itself.  Messing around with load balancing algorithms might be more expensive than violating the low-latency guarantee on a few requests.  And the list goes on and on – so caveat lector.

Another example is an application that is sensitive to GC timing but has lots of idle periods with no significant work.  Such an application might want to trigger garbage collections “manually” by calling GC.Collect during idle times if it is notified that GC is approaching.  This is again risky because if your application’s allocation patterns are not uniform, you might be triggering a collection too early or too late.

However.  You’re not here for my lame advice telling you that every single pattern out there is wrong.  The new APIs have been added for a reason – they give you a choice.  Let’s take a look at this choice.

  1. By calling GC.RegisterForFullGCNotification, an application indicates that it is willing to poll the CLR for a GC approaching.
  2. In a separate application thread, the application then calls GC.WaitForFullGCApproach which returns a value of the GCNotificationStatus enumeration (this wait can also be associated with a timeout).  The status can be many things, among them GCNotificationStatus.Succeeded indicating that a full GC is near. 
  3. The application now proceeds to perform a collection manually, balance the work load to other instances, notify the user – anything of interest, really – and then calls GC.WaitForFullGCComplete.
  4. The loop then repeats until at some point someone calls the GC.CancelFullGCNotification method and stops the polling thread.

This is shown in the following simple code snippet:

GC.RegisterForFullGCNotification(10, 10);

Thread monitor = new Thread(() =>


    while (true)


        GCNotificationStatus status = GC.WaitForFullGCApproach();

        if (status != GCNotificationStatus.Succeeded)


            //Canceled, invalid etc.


        //Redirect load, call GC.Collect yourself etc.

        status = GC.WaitForFullGCComplete();

        if (status != GCNotificationStatus.Succeeded)


            //Canceled, invalid etc.







(The above code assumes that the DoAllocations method performs enough allocations to trigger a full GC.)

To test this, you will have to ensure that you’re running under non-concurrent GC (or else, attempting to register for notifications will throw an exception).  The easiest way to do so is by creating an app.config file and adding to it the following configuration element:



    <gcConcurrent enabled="false" />



Note that the GC.RegisterForFullGCNotification method takes two integer parameters, which can be in the range 1-99.  These values (thresholds) affect the eagerness of the notification: a high value means you are more likely to be notified when a GC is approaching, but that more time might pass until the GC actually occurs; a low value means you might not get the notification in time to do your magic before the GC begins and suspends all threads (remember, this is non-concurrent-GC-land).

Why are there two values?  The first one controls the notification eagerness for the small object heap (generation 2) and the second controls the notification eagerness for the large object heap.

Summing this up, as of .NET 3.5 SP1 there’s yet another way to interact with the .NET garbage collector, and it is not recommended to start messing around (as always :-)) unless you are absolutely certain that the performance characteristics of your application are highly sensitive to garbage collection timing.

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>



  1. David NelsonAugust 25, 2008 ב 2:47 PM

    So every single application that wants to use this API is required to use up a thread doing nothing but waiting for a GC to occur? Why not have RegisterForFullGCNotification take a delegate which is called on a new thread?

  2. EnlargementJanuary 15, 2009 ב 11:20 PM

    I am amazed with it. It is a good thing for my research. Thanks

  3. FlallautotaJanuary 20, 2009 ב 10:03 PM

    I am unable to understand this post. But well some points are useful for me.