As a component vendor we need to be extra cautious about the quality of the code we ship. Design, presentation, performance etc. are all very important for us. One item that is often overlooked in the .NET community is memory leaks. It is a quite a precarious issue, because most people believe that in the managed world it is not possible to have a memory leak. Well, if you thought so you are in for a surprise.
The idea of the blog post is not to explain what memory leaks are, how the garbage collector works and so on. These things have already been explained very nicely here. What I will try to offer is some tips that could make your life easier while dealing with the leak phenomenon. For starters you will need to download and install WinDbg. Once you have windbg up and running you can begin your first hunt :-).
First of all you will need to load SOS.dll. This is a debugging extension that helps you debug managed programs in WinDbg or VisualStudio. More info can be found here. What is most important to know, however, is that there are different versions of this DLL for different versions of the CLR. One for .NET 1.0, one for .NET 2.0+ and one for Silverlight. To load the extension you need to type the following:
For WPF: .loadby sos mscorwks
For Silverlight: .load C:\Program Files\Microsoft Silverlight\2.0.31005.0\sos.dll (at the time of this writing this is the RTW version of SIlverlight 2.0)
The next step is to run the application or browser and attach to it from within WinDbg. You can do this by pressing F6. Make sure, however, you are not running the app from within VisualStudio with F5 because you will not be able to attach windbg as visual studio has already attached itself as a debugger for the application.
Next you would want to walk through the managed heap and find out what objects are allocated there. You can do this by issuing the following command: !dumpheap –stat. What this will do is list all allocated types, the number of times they are allocated and the total size of each type on the stack. I find it useful to filter through the results if I suspect a certain type leaking. You can do this in the following way:
.shell -i - -ci "!dumpheap -stat" findstr "NameOfSuspectedTypeHere"
This is a little hack that will let you use the windows shell findstr command to filter lines which do not contain the string in question.
Now that we have an initial measure of the instances of the type in the application we can type “g” in the command prompt and perform some actions that will make the program leak. After we caused some allocations to take place it is a good time to make a call to GC.Collect(). For this reason I usually end up adding a Button control to a major form so that I can press it easily at any time. In its click handler you will have to call GC.Collect() to force the garbage collector to reclaim all instances which are not in use by the application.
When we are satisfied with the results we can break again in the debugger by pressing Ctrl+Break. Now it is time to inspect our type again in the same fashion as described above. If the suspected type has significantly more (it really depends on the logic of your application) instances you have caught a leak!
Our next step will be to inspect all instances of the offending type. This is easily done by typing: “!dumpheap –type NameOfTypeHere’. It will list all instances of this type on the managed heap. I would usually pick one high in the list as memory addresses grow going downward and there is a higher chance to catch a leaking type on the top of the list.
Once you have chosen an object, grab its address (by selecting and right-clicking it windbg will copy it to the clipboard) and issue the !gcroot <address> command. It will list the path of the object to the root of the garbage collector. This is where you will do your detective work. Very often you will find out that it is a unregistered event handler keeping a strong reference to the leaking object. Some other times it may be something else. In most cases, however, I have found this information to be enough to pinpoint the culprit and change my code to eliminate the leak.
This about covers the basics. There is a lot of information out there to help you out further. Some great blogs I frequent are:
http://blogs.msdn.com/tess/default.aspx - If broken it is, fix it you should
http://blogs.msdn.com/johan/default.aspx - Speaking of which...
http://blogs.msdn.com/debuggingtoolbox/default.aspx – Debugging toolbox
http://dotnetdebug.net/ – DotNetDebug
Feel free to comment if this has helped you or if you want to share some of your own tips.
Vladimir Milev is a developer manager.