I have a WPF application that is experiencing a lot of performance issues. The worst of them is that sometimes the application just freezes for a few seconds before running
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.
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.