How do you determine whether the memory issue you seem to be experiencing is a .NET memory leak or a native memory leak? Why is it even important?
It’s the first question I as a consultant would ask you if we were to talk over the phone regarding a high-memory problem you’ve having. It’s extremely important because in most of the cases it narrows the scope of the problem and facilitates bringing the appropriate people, tools and resources for focusing on the task at hand.
The process for diagnosing a .NET memory leak revolves around tools such as the CLR Profiler, WinDbg, SOS.DLL and the useful set of commands such as !VMStat, !DumpHeap, !TraverseHeap, !GCRoot and others. The process for diagnosing a native memory leak, on the other hand, is usually significantly more complicated and vague, consisting of analyzing the memory for patterns, searching strings in memory, attempting to correlate memory usage to modules, and so on.
So how do you tell? The first step that you’re definitely capable of taking in the right direction is opening Performance Monitor and looking at the values of two important performance counters for your application’s process:
- .NET CLR Memory / # Bytes in All Heaps
- Process / Private Bytes
The private bytes counter for your process is the amount of memory committed to the process by the Windows memory manager. It doesn’t have to reside in physical memory – it might be paged out or in transition – but it does reflect the memory usage of your application. Note the “private” in the counter’s name – only memory that isn’t shared with other processes on the system is considered for this counter’s value. Shared memory sections or loaded assemblies are not considered.
The .NET bytes in all heaps counter is the amount of managed memory allocated in your process (for all AppDomains). This is garbage-collected memory, and a leak here indicates that we can use the standard arsenal for managed high-memory scenarios.
There can really be three distinct scenarios from this point on:
- The private bytes counter is climbing but the .NET bytes in all heaps counter remains constant. This indicates a native memory leak.
- The private bytes counter and the .NET bytes in all heaps counter are climbing at the same rate (the difference between the two remains constant). This indicates a managed memory leak.
- The private bytes counter and the .ET bytes in all heaps counter are climbing at different rates (the difference between the two is not constant). This indicates a combined managed and native memory leak, e.g. leaking a managed component that holds a reference to a COM object (via an RCW) or creating a CCW/RCW cycle.
Let’s take an example from one of my recent debugging consultations. At the client’s site I’ve set up a performance counters log to monitor the above two counters and determine what kind of leak we’re talking about. Here’s what the graph looked like:
The black highlighted line is the managed memory counter, the blue line is the private bytes counter.
From this graph, it’s evident that the applications is leaking managed memory only. The difference between the two counters stays constant, meaning we’re in leak category #2 above. This enables a smooth transition to dump analysis for managed leaks.