Finding Memory Leaks In Silverlight With WinDbg
Posted by Davy Brion on August 8th, 2009
As i mentioned in a previous post, you can attach WinDbg to a browser to find memory leaks in your Silverlight applications. I figured it would be a good idea to write down how this process works, since i always end up having to look it up again whenever i need to do this.
I wrote a very simple Silverlight application which has a rather typical memory leak. Here’s the actual code:
public event EventHandler<MyEventArgs> MyEvent = delegate { };
private void CreateNewViewButton_OnClick(object sender, RoutedEventArgs e)
{
ViewContainer.Children.Clear();
var newView = new MyView();
MyEvent += newView.EventHandler;
ViewContainer.Children.Add(newView);
}
For some of you, the memory leak is already very clear. Like i said, it’s a very simple example
Let’s go through the process of finding and fixing the memory leak using WinDbg. First of all, download Debugging Tools For Windows (which contains the WinDbg executable) here and install it.
Then we start our application in Internet Explorer (for some reason i can’t use WinDbg to inspect the managed memory heap with Firefox, so i just use Internet Explorer for this stuff) and use it. In the case of my example, that means clicking the button which creates a new view a couple of times.
Open WinDbg.exe and select the ‘Attach to a Process’ menu item in the ‘File’ menu and select the iexplore.exe process.
Then you need to load the correct version of sos.dll:
After that we can see which types of our MySilverlightApplication namespace are present in the managed heap, including how many instances of them:
As you can see, there are 13 instances of our MyView type present in the heap. Using the value in the MT column, we can drill down further:
This shows the memory address of each instance of the MyView type in the heap. Now we can see if there are any live references to these instances:
This is actually for the first address that was listed. As you can see, it is still a reachable reference, which means it will not be collected by the garbage collector. The chain of references clearly indicates that the instance is still referenced from our event in the MainPage instance. All of the previously listed instances show the same reference chain, so this is clearly a memory leak. Even though we should only have one active reference of MyView at any point in time of this application, the MyEvent event on MainPage clearly keeps each instance of MyView alive.
The correct way to fix this is to make sure that whenever we remove an instance of MyView, we need to unsubscribe it from the MainPage’s MyEvent handler. Always remember this rule when it comes to dealing with events: if the publisher of the event has a longer lifetime than the subscriber of the event, then you absolutely have to unsubscribe each subscriber from the event or the publisher will keep references to each subscriber (preventing them from being garbage collected) for as long as the publisher is alive.
Here’s the modified version of the above code which avoids the memory leak:
public event EventHandler<MyEventArgs> MyEvent = delegate { };
private void CreateNewViewButton_OnClick(object sender, RoutedEventArgs e)
{
CleanUpPreviousView();
var newView = new MyView();
MyEvent += newView.EventHandler;
ViewContainer.Children.Add(newView);
}
private void CleanUpPreviousView()
{
if (ViewContainer.Children.Count > 0)
{
var myView = ViewContainer.Children[0] as MyView;
if (myView != null) MyEvent -= myView.EventHandler;
ViewContainer.Children.Clear();
}
}
Let’s see if this really fixed the memory leak. If we fire up the application and press the button a couple of times, the application should normally only have one live reference of MyView in memory.
I clicked the button 5 times, and the above output shows that there are 5 instances of MyView on the heap. So did we fix the leak or not? Check the output below:
As you can see, only the last instance of MyView is actively referenced somewhere. That means that the first 4 instances are ready to be collected during the next garbage collection.
One thing i don’t understand though, is that the reference chain of the last instance doesn’t mention MainPage or the event handler anymore. But when i attached Visual Studio’s debugger to the browser instance i could clearly see that the MyEvent of MainPage indeed contained an event handler that pointed to this MyView instance. I’m far from a WinDbg and SOS expert so i have no idea why the reference chain doesn’t reflect this. Perhaps someone with more WinDbg and SOS knowledge can shed some light on this?
Either way, this approach is a pretty good way of finding memory leaks in your Silverlight code. In a real application it’s obviously a bit more complicated to find the exact cause of a leak compared to this simple example, but it’s still pretty doable. Just execute the !dumpheap -stat -type YourRootNameSpaceHere and look for unusually high numbers of instances of your types. Then you can start looking at each instance to figure out what’s going on. And for a nice list of commands that you can execute in WinDbg with SOS, be sure to check this out.
Also, keep in mind that you can do this for every .NET process, and not just Silverlight. Though you would need to load the sos.dll file of your particular .NET version.
August 10th, 2009 at 1:02 am
Thats so helpful. I am looking forward to see more articles.
http://aspnetcsharp4.blogspot.com/
Thanks
Kulveer
August 10th, 2009 at 2:37 am
[...] Finding Memory Leaks In Silverlight With WinDbg Published Sunday, August 09, 2009 8:37 PM by gOODiDEA Filed under: SQLServer, WinDbg, Utility, Performance, Memory Leak, Silverlight, Tips [...]
August 10th, 2009 at 6:58 am
Very informative! I guess I have a lot to debug.
August 10th, 2009 at 9:40 am
[...] Finding Memory Leaks In Silverlight With WinDbg – Davy Brion shows how you can use WinDbg to identify memory leaks in your Silverlight Applications by attaching the debugger to your browser instance [...]
September 8th, 2009 at 4:46 pm
hello ,
thats a great article with a cool example.
you have paved a way to lots of bubbling debug amatuers like me .
please keep your good work going .
thank you .
BooTCaT,
working for verizon
November 2nd, 2009 at 11:53 pm
[...] Of course, I've left out the implemenation of fetching this, but you get the point. While that is an interesting example, how about a more practical one? One of the most common causes of memory leaks in Silverlight/WPF applications is related to the registering and unregistering of events. To learn more about this memory leak (and also learn how to troubleshoot/debug/and find the source of the leak), read this article about finding memory leaks in Silverlight. [...]
December 23rd, 2009 at 11:05 am
Hi Davy,
This information is definatly pointing me in the right direction but I am getting what you had at the end of your demo:
0:032> !gcroot -nostacks 07a45abc
DOMAIN(06BF2C98):HANDLE(Pinned):64312f8:Root: 089e4260(System.Object[])->
079e5e38(System.Collections.Generic.List`1[[System.Object, mscorlib]])->
07a4ad64(System.Object[])->
07a45abc(BombDrop.Bomb)
For each of the objects that I have I expect them to be free. I take it from the word (Pinned) that something still has a handle to this object.
Do you know how to find out exactly who the who is?
Regards
Dewy
December 23rd, 2009 at 1:19 pm
@Dewy
try the last couple of steps mentioned in this post:
http://blogs.msdn.com/delay/archive/2009/03/11/where-s-your-leak-at-using-windbg-sos-and-gcroot-to-diagnose-a-net-memory-leak.aspx
i’ve used that a couple of times, sometimes it worked but sometimes it didn’t. doesn’t hurt to try though