For the past few days I’ve been investigating some memory leak issues in our desktop application. The problem started showing up when we saw that opening new documents and then closing them didn’t have any negative impact on the memory usage. Initial tests using vadump and process explorer confirmed that there was an issue and so we the developers started looking into it.
Initially it looked like the problem was that certain event handlers were causing references to closed documents to hang on a couple of ones that I remember are Application.Idle and SystemEvents.PowerModeChanged. Next there were some references via event handlers that were being held by Singleton objects and some service objects and those were easily handled as well.
After this we could see that the references were still hanging around, btw we were using a combination of ANTS profiler, WinDBG (dumpheap + gcroot) and the VS.Net debugger all this while to investigate the issue. After fixing the obvious issues we struggled to find the root cause of the memory leaks. Then I looked around for alternate profilers and came across .Net Memory Profiler from SciTec, using this gave a much clearer picture into the issue, you see the new profiler gave you allocation stacks for all references and the ability to reflect over the instance fields, using this I started seeing that two third-party components that we were using were causing the issue.
Basically both third-party components, one a very well known UI toolkit and another one that provides skinning support to controls were storing references to controls in static hashtables. In one toolkit a bug in the code caused the reference from the hashtable to remain even after it was not required and in the second case I think those guys just didn’t know how to remove an entry from a hashtable, they were simply setting the value of the key to be null causing a reference to the key to be held by the static hashtable.
We now need to hack around these issues either by using Reflection or getting an updated build from the vendors.
Some of the lessons I’ve learnt from this
- Always, Always have source code for any third-party component that you are using in your application. For any non-trivial usage you’ll always end up fixing bugs in the component.
- When putting any data in static fields, double check to make sure that it’s really required and keep in mind the memory impact of the decision also provide a clean API to clean up the static data.
- If your’re hooking onto an event then make sure to unhook when it’s no longer required.