Monitoring Garbage Collector in C#

I don’t see GC.RegisterForFullGCNotification(int,int) anywhere in your code. It looks like you’re using the WaitForFullGC[xxx] methods, but are never registering for the notification. That’s probably why you’re getting the NotApplicable status.

However, I’m doubting that GC is your problem, while possible, I suppose it would be good to know about all of the GC modes there are and the best ways to determine what is happening. There are two modes of Garbage Collection in .NET: the Server and Workstation. They both collect the same unused memory, however the way it’s done is ever so slightly different.

  • Server Version – This mode tells the GC for you’re using a server side application, and it tries to optimize collections for these scenarios. It will split the heap into several sections, 1 per CPU. When the GC is started, it will run one thread on each CPU in parallel. You really want multiple CPUs for this to work well. While the server version uses multiple threads for the GC, it’s not the same as the concurrent workstation GC mode listed below. Each thread acts like the non-concurrent version.

  • Workstation Version – This mode tells GC you’re using a client side application. It figures you have more limited resources than the Server version, and so there is only one GC thread. However, there are two configurations of the Workstation version: concurrent and non-concurrent.

    • Concurrent – This is the version turned on by default whenever the workstation GC is used (this would be the case for your WPF application). The GC is always running on a separate thread that is always marking objects for collection when the application is running. Furthermore, it chooses whether or not to compact the memory in certain generations, and makes that choice based on performance. It still has to freeze all threads to run a collection if compacting is done, but you will almost never see an unresponsive application when using this mode. This creates a better interactive experience for uses and is best for console or GUI apps.
    • Non-Concurrent – This is a version you can configure your application to use, if you’d like. In this mode, the GC thread sleeps until a GC is started, then it goes and marks all object trees that are garbage, frees the memory, and compacts it, all while all other threads are suspended. This can cause application to sometimes become unresponsive for a short period of time.

You can’t register for notifications on the concurrent collector, since that’s done in the background. It’s possible that your application is not using the concurrent collector (I notice you have the gcConcurrent disabled in the app.config, but it seems that’s only for testing?). If that’s the case, you can certainly see your application freezing if there’s heavy collections. This is why they created the concurrent collector. The type of GC mode can partially be set in code, and fully set in application configurations and machine configurations.

What can we do to figure out exactly what our application is using? At runtime, you can query the static GCSettings class (in System.Runtime). GCSettings.IsServerGC will tell you if you’re running the workstation on server versions and GCSettings.LatencyMode can tell you if you’re using the concurrent, non-concurrent or a special one you have to set in code which isn’t really applicable here. I think that would be a good place to start, and could explain why it’s running fine on your machine, but not production.

In the configuration files, <gcConcurrent enabled="true|false"/> or <gcServer enabled="true|false"/> control the modes of the garbage collector. Keep in mind this can be in your app.config file (located aside the executing assembly) or in the machine.config file, which is located in %windir%\Microsoft.NET\Framework\[version]\CONFIG\

You can also remotely use the Windows Performance Monitor to access the production machine’s performance counters for .NET garbage collection and view those statistics. You can do the same with Event Tracing for Windows (ETW) all remotely. For performance monitor, you’d want the .NET CLR Memory object, and select your application in the instances list box.

Leave a Comment