WPF & Memory Leaks

The Red Herring:

Recently, I had to track down why all of our new WPF screens were leaking memory like a sieve. A coworker of mine ran into a discussion on the WPF forum and my jaw dropped. I rapidly dove into this issue and soon discovered that this issue was not really the one causing us problems (that I could tell). It seems as if this issue only raises its ugly head in certain situations (see the post for more detail).

The Awesome Tool:

memprofiler.png

At this point, we ran into a tool that, in my opinion, is a must have if you are trying to find memory leaks in a .NET application. It is called (aptly enough), .NET Memory Profiler and it is made by a company called SciTech software. Previously, we had been using AQtime and I still would recommend that tool for performance profiling but when it comes to memory allocation profiling (i.e. memory leaks), it is quite slow. The .NET Memory Profiler doesn’t do performance profiling and focuses exclusively on memory allocation profiling. I think this has made it the best tool out there. I have tried Jet Brains dotTRACE as well, which is also very fast, but the .NET Memory Profiler beats that one hands down in how it presents the information to the user and how it allows you to navigate through all the objects that have been created or are still living on the heap. They do offer a free 14-day trial and I would recommend that you try their 3.5 version which you can preview now. This version has a very nice feature that they call Automatic Memory Analysis, which is basically tips and warnings regarding common memory usage issues.

The Real Issue:

Using this tool and some grit, the previously mentioned coworker and I finally ran into the issue that was causing us problems. It is a known issue. Personally, I love what it says at the knowledge base article just referenced:

When you use data binding in Microsoft Windows Presentation Foundation (WPF), a memory leak may occur.

Ha! Are you kidding me?!

You can check out the knowledge base article for more info, but basically, if you are binding to something that isn’t a DependencyProperty or doesn’t implement INotifyPropertyChanged, WPF tries to use the ValueChanged event. In doing so, it calls PropertyDescriptor.AddValueChanged which creates a strong reference from the property descriptor to the object being bound to.

Something as simple as the following xaml causes a leak:

<Label Name="MyLabel">
    <Stack Panel Name="MyStackPanel">
        <TextBlock Text="{Binding ElementName=MyStackPanel, Path=Children.Count}" />
    </StackPanel>
</Label>

One way that you can verify that this issue is causing you problems is to see PropertyDescriptor in the root path. Check out this screen capture:

rootpath.png

The Solution:

So, how did we rid ourselves of this problem? Well, we basically went in and modified the objects that were causing us leaks to inherit from INotifyPropertyChangedeven if we didn’t really want to notify anyone of changes. In the those cases, we would #pragma the CS00067 warning away that came from not using the PropertyChanged event:

public class SomeClassBeingDataBoundTo : INotifyPropertyChanged
{
    // class implementation

#pragma warning disable 0067
    // If you data-bind to a regular .NET property on a regular .NET object, that object will never get garbage-collected.
    //    •    Dependency properties are not affected.
    //    •    Objects implementing the INotifyPropertyChanged interface are not affected.
    public event PropertyChangedEventHandler PropertyChanged;
#pragma warning restore 0067
}

How the heck did I miss this?

So, after all the excitement subsided, I did a Google search to see if I could find any other blogs that talks about this problem.  Heh heh. The link at the top of search results … was about our issue. Argh! I don’t know how I missed it before when I was doing my original research.

Even better, the search results brought up Jossef Goldberg’s priceless post on finding memory leaks in WPF applications (that I’ve run into before). That post is a must read if you are trying to find leaks in your WPF applications. And, yes, it too, talks about our issue.

Someone, please take my software engineering card away.

I hope my pain is someone else’s gain.

10 thoughts on “WPF & Memory Leaks”

  1. Nice blog post Cory. Have you tried the latest ANTS Memory Profiler from Red Gate? It’s a major improvement over what it used to be, but you should judge for yourself as I’m a bit biased.

  2. I have not tried Red Gate’s ANTS Memory Profiler. I stopped trying different ones out once I ran into SciTech’s .NET Memory Profiler. I’ll have to give it a look.

  3. Very good post Cory! I have also managed to fall foul to this subtle bug too… I cannot believe this is not *the* major issue for WPF out there (maybe people aren’t really shipping real world apps in WPF yet?) Here’s my very simplified scenario:

    I have resource dictionaries:

    Colors.xaml – with:
    <Color x:Key="BackgroundColor">#FF6699CC</Color>

    Brushes.xaml – with (note dynamic and static resource references in color prop):
    <SolidColorBrush x:Key="DynamicBackgroundBrush" Color="{DynamicResource BackgroundColor}"/>
    <SolidColorBrush x:Key="StaticBackgroundBrush" Color="{StaticResource BackgroundColor}"/>

    I have UserControl1.xaml and Usercontrol2.xaml.

    In user control 1 I use DynamicBackgroundBrush for a border background.
    In user control 2 I use StaticBackgroundBrush for a border background.

    In Window1.xaml
    I use a button to swap the ContentProperty of a ContentControl to either UserControl 1 or 2 to toggle between the two (like one would using CAL/PRISM if you use ContentControl regions to present different views).

    if(_userControlPlaceholder.Content is UserControl1)
    _userControlPlaceholder.Content = new UserControl2();
    else
    _userControlPlaceholder.Content = new UserControl1();
    The result is that UserControl1 leaks and UserControl2 does not. This seems to be due to the fact that Color <- SolidColorBrush <- Border.Background where the refs are all DynamicResource refs.

    Bear in mind that this is a simplified scenario to show the issue and that it is especially subtle when control templates are involves which exist in separate resource dictionary files in which it is sometimes difficult to use a StaticResource reference.

    What has been your workaround to this? I have verified that it is fixed in .NET 4.0 (Beta) but what to do for now?

    BTW – .NET Memory Profiler flagged this immediately with a ‘Direct EventHandler roots’ warning – great tool!

  4. @Clint

    Thanks Clint, glad you liked the post. I wrote it so that my pain could be other people’s gain.

    As I understand your comment, it seems as if you are running afoul of what I was calling the ‘Red Herring’. It is definitely a bug, and one (as you have noted) that they have fixed. However, I could never verify that it was causing us issues in our running application. In every place that I checked, inflation of the resource caused it to detach and not leak.

    It seems as if you found a situation where it still leaks even if the UserControl becomes part of the visual tree. Well, I guess I am assuming that _userControlPlaceholder is being shown on the screen … is that the case?

    Regardless, all that being said, I would say that the workaround would be to use StaticResource(s) instead of DynamicResource(s). Of course, this would eliminite the possibility of ‘on-the-fly’ skinning. We ourselves just left things the way the are … as the big leak by far … was the one that occurs with data binding.

    Good luck … thanks for commenting!

  5. Red Gate were kind enough to send me a free copy of their ANTS profiler for review. Unfortunately, I never published the review in our literature because it just crashed over and over. Turns out it does not fully support .NET CIL and many of our programs use ILX that causes the ANTS profiler to crash.

  6. Eric Harding of Microsoft says that they use the .NET Memory Profiler internally on the WPF performance team.

    Check out the following PDC session where he states that. For that matter, that session is just a great session on WPF performance problems and how to use tools to solve them.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.