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 https://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 (https://blogs.msdn.com/micmcd/archive/2008/01/22/inotifychanged-magic-also-of-the-black-arts.aspx).
Comments
Anonymous
March 07, 2008
PingBack from http://msdnrss.thecoderblogs.com/2008/03/07/avoiding-a-wpf-memory-leak-with-databinding-black-magic/Anonymous
March 28, 2008
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, -SzymonAnonymous
July 25, 2008
The comment has been removedAnonymous
November 14, 2009
The comment has been removedAnonymous
March 30, 2015
Hi, Thanks for this excellent post!