Monitoring Garbage Collector in C#

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.

How to know if a gen full garbage collection has been triggered between two checkpoints?

The "RegisterForFullGCNotification" method registers for a notification to be raised when the runtime senses that a full garbage collection is approaching. There are two parts to this notification: when the full garbage collection is approaching and when the full garbage collection has completed.

To determine when a notification has been raised, use the "WaitForFullGCApproach" and "WaitForFullGCComplete" methods. Typically, you use these methods in a while loop to continually obtain a "GCNotificationStatus" enumeration that shows the status of the notification.

The "WaitForFullGCApproach" and the "WaitForFullGCComplete" methods are designed to work together. Using one without the other can produce indeterminate results.

c# Garbage Collection. .NET CLR Memory Perfmon counters showing 0 for gen 0 heap size etc. What does this mean?

I would put my money on 1) A garbage collection has not taken place yet.

I've compared the performance monitor output of the following two scenarios:

static void Main(string args[])
{
Console.ReadLine();
}

In this case, the performance monitor shows 0 bytes in all heaps.

with this which shows 22,496 bytes in all heaps.

static void Main(string args[])
{
GC.Collect();
Console.ReadLine();
}

This would suggest that even an empty app in which no memory is directly allocated has some heap allocations.

You could test this out by placing a call to GC.Collect somewhere in your code.

Garbage Collection Across C# and C++/CLI Objects

When the code is actually running, none of it will be C# or C++/CLI. All of it will be IL from the C# and C++/CLI and machine code from the native code you're interoperating with.

Hence you could re-write part of your question as:

  • Assembly A - (IL and we don't know what it was written in)
  • Assembly B - (IL and we don't know what it was written in)

Of the managed objects, all of them will be garbage collected as per the same rules, unless you use a mechanism to prevent it (GC.KeepAlive). All of them could be moved in memory unless you pin them (because you're passing addresses to unmanaged code.

.NET Profiler will give you some information on garbage collection, as will the collection counts in performance monitor.



Related Topics



Leave a reply



Submit