Avoiding a WPF memory leak with DataBinding (Black Magic)


A quick note that can save you a lot of pain later: I mentioned a while back that binding to an ObservableCollection<T> is preferable to binding to a List<T>.  The reason was that changes to an ObservableCollection are immediately handled by the object to which it is bound, unlike a list.  Behind the scenes, the reason for this is that ObservableCollection implements the INotifyPropertyChanged interface.  Ordinarily I'd be quite happy to let the black magic do its work, but there is a huge pain point related to INotifyPropertyChanged.

WPF has a sadly unavoidable memory leak related to data binding, and the problem is greatly exacerbated by Blend.  For specifics, you can look at http://blogs.msdn.com/jgoldb/archive/2008/02/04/finding-memory-leaks-in-wpf-based-applications.aspx .  The leak isn't an awful oversight by the WPF team -- it is caused when databinding to any CLR object, and most data sources stick around for the lifetime of the app, so it isn't regarded as a horrible thing to hold onto (the memory is released when the app is closed).  In Blend, however, if you don't play by very specific rules you can leak the entire visual tree every time you make a change, such as when you go to edit a template, build, make an animation, and many other cases.  In each instance, Blend is making WPF's quasi-mistake over and over.  With a decently sized project, it is trivial to get Blend to be using upwards of 1GB(!!!) of memory simply by making repeated edits to a template.  Blend rebuilds data sources quite frequently when making certain types of edits.  I stumbled upon this issue quite accidentally.

How to fall into the trap:

You have a ListBox in your project bound to an ObservableCollection<MyClass>.  You're quite happy with yourself for using an OC rather than a List, and now whenever you add another instance of MyClass to the OC, you see it in your listbox.  You have a TextBox, however, whose data context is bound to the selected item of the listbox.  The textBox's text is bound to the Foo field of the Data Context, which is a MyClass.  You wouldn't ordinarily think that you'd need to have MyClass know anything about databinding, or to implement any wacky interfaces or anything of the sort.  Maybe the Foo field can't even be changed, only displayed -- so why bother implementing INotifyProperyChanged? 

What happened:

There is an issue where WPF checks to find things that implement INotifyProperyChanged.  If there is a databinding to something not implementing this interface, then it makes a record in a global table.  That record doesn't get cleaned up, as WPF has no way of checking when that DB record is no longer needed.  Everything could work perfectly in your app when you run it, but if you make certain kinds of edits in Blend on the project you can leak memory like... hmmm... like a wet diary?  Did that simile work?  Anyhow...

To Fix:

Anything involved in a databinding should implement INotifyPropertyChanged.  You don't have to raise the PropertyChanged event if you don't particularly feel like it, but to avoid the leak you have to implement the interface.  The annoying side effect here is that it can lead to lots of squiggly green lines in Visual Studio nagging you about declaring an event you haven't done anything with (you have to declare the PropertyChanged event to implement the interface), but this doesn't hurt your code (other than aesthetically).  Add a few lines of useless code, and you won't have to deal with an incredibly sluggish Blend.

Note that, in order to avoid the memory leak you just have to implement the interface.  If you actually want to use it, you need to make calls to NotifyProperyChanged as I mentioned a while back (http://blogs.msdn.com/micmcd/archive/2008/01/22/inotifychanged-magic-also-of-the-black-arts.aspx). 


Comments (5)

  1. skobalczyk says:

    Hi Michael,

    Thanks for this excellent post! I'm actually looking at memory issues in our application right now, and this information is very helpful. Could you tell me more of the tools and methods you use to diagnose such problems? I mostly dig around with WinDbg+Sos but maybe there are better ways to do this.

    Regards,

    -Szymon

  2. MSDNArchive says:

    Hi Szymon,

    Sorry this took a crazy long time to get back to you -- I switched teams not too long ago and hadn't gotten to my old blog in a while.  

    To answer your question, I'm afraid I wasn't using anything exciting -- just the windows task manager.  With a fairly highly databound project, I could pretty easily repeat an operation that would increase the amount of memory used by about 30000k each time.

    Michael

  3. lucho says:

    I have a VM that exposes dtaviews and datarowviews. Interetinly enough it works perfectly the first time around, changes in one ui control reflect on other ui controls as their are supposed to.

    But the user has the option to close and reopen the same wpf window from context menu, anyhow the second time around the bindings do not sycrhonize anything at all.

    So this got me thinking that if it works the first time around and it does not work the second time around, it is probably a memory leak of some sort.

    Has anyone experience similar situations? how can I debug second time around issues? in particular when binding to dataviews, etc. I have not even be able to visualize the user even when having the debugConverter or the trace set to high. Maybe I need to look into the logical tree or somenthing.

    Any ideas on this particular issue, will be greatly appreciated

  4. Hazholhim says:

    Hi,

    Thanks for this excellent post!

Skip to main content