Tuesday, 16 October 2012

WPF and Silverlight Code Performance

As WPF and Silverlight sit on the .NET framework, they’re subject to the rules of the Garbage Collector. That means there are a few unique ways in which WPF will cause your application to leak memory, and Chris Farrell points out the most prominent culprits.

As I’m sure you know, WPF and Silverlight both use XAML as their interface markup language. The idea is that you can define your user interface (UI) and bind it to data without ever writing a line of code (healthy skepticism advised). Whether or not you buy into that vision, the UI possibilities can be stunning, and it seems Microsoft has created a technology that combines both power and flexibility. However, with that power comes responsibility.

It’s with an eye on that responsibility that I write this article, in which I want to talk about some of the problems that you can introduce into your application without even realizing it.

Background

.NET uses a garbage collector to reclaim and reuse the space left behind when objects are no longer needed. To do this, it builds a list of all objects that are still ultimately referenced from an application root, such as the stack, other objects on the heap, the CPU and statics, to name just a few. Everything else (i.e. objects which have no such references) is assumed to be garbage, and the .NET framework rearranges memory allocation to reuse the gaps these objects filled.

A leak (or, if you’re being picky, leak-like behavior) occurs when a section of code fails to release references to objects it has finished working with. The smaller the leak, the greater the number of iterations that must occur before it becomes a noticeable problem. The larger the leak, the more obvious the problem.

A really obvious example of this problem is adding an object to a static collection and then forgetting about it. Other common ones involve event handling, which we will discuss later. The simple fact is that if you leave a reference to an object behind, and that reference traces back to an application root, then you have a leak.

 

heavyweight User Interfaces in xaml

Silverlight and WPF applications are state-full, and allow us to hold state in the form of complex data structures as well as rich UI elements such as images and media. All of this “stuff” adds to the size of the views we create and, ultimately, the size of a memory leak when things go wrong. If you have a memory leak that involves a complex UI then it can quickly become a major problem, especially if users are constantly opening and closing windows as part of standard flows.

Just as an example of how the way these problems can scale, this is exactly the situation I found with a large financial application written in WPF, employing all the usual accounting/finance type windows, often containing many hundreds of rows of data. As you would expect, the developers had taken full advantage of data binding and the entity framework. It looked great and all seemed well until, during system testing, they discovered that the application would get slower over time and ultimately crash the machine. Eventually they actually had to reboot to get over it. Naturally I suspected a memory leak, but nothing could prepare me for the extent of the issues I actually found (but that’s a different story).

Some of the issues we’ll cover are specific to WPF/XAML and Silverlight, and others are general leaks you will get in any application. I thought it would be useful to go through the main technology-specific leaks you can easily create; thankfully, the good news is that they are easy to fix and avoid in the future.

WPF and Silverlight leaks

While I‘ve tried to come up with a list of the most likely leaks, the trouble is that, depending on platform and framework versions, there are many potential leak mistakes you can make. Regardless, you’ve got to start somewhere, and these points will always serve you well. You’re likely to quickly see a pattern emerging in the underlying nature of the problems and solutions I highlight, but I do recommend you read to the end, because I almost guarantee that you’ll encounter one or more of these situations sooner or later.

Unregistered events (WPF + Silverlight, All versions)

Let’s start with the classic leak, common to all .NET applications - the event leak. While this is a common source of leaks for all .NET applications, it’s not a bug in .NET, but rather a common oversight by developers.
Specifically, if you create an event handler to handle events occurring in some object, then if you don’t clear the link when you have finished, an unwanted strong reference will be left behind.

The Issue

My contrived example below deliberately isn’t specific to WPF/Silverlight, but I include it because it’s a very common memory leak which all .NET applications are vulnerable to. In it I am subscribing to an OnPlaced event on my Order class. Imagine this code executes on a button click. Basically, it sets up an order for a currency exchange to take place when certain price conditions are met:

Order newOrder=new Order(“EURUSD”, DealType.Buy, Price PriceTolerance, TakeProfit, StopLoss);
newOrder.OnPlaced+=OrderPlaced;
m_PendingDeals.Add(newOrder);

Listing 1

When the price is right, an Order completes and calls the OnPlaced event, which is handled by the OrderPlaced method;

void OrderPlace(Order placedOrder)
{
      m_PendingDeals.Remove(placedOrder);
}
Listing 2 

In the event handler, you can see that we are already eliminating a really common source of leaks; namely, references from collections (in this case the m_PendingDeals collection).

However, the OrderPlaced event handler still holds a reference to the Order object from when we subscribed to the OnPlaced event. That reference will keep the Order object alive even though we have removed it from the collection. It’s so easy to make this mistake.

The Solution

The OrderPlaced method is just one line away from avoiding a memory leak!

void OrderPlaced(Order placedOrder)
{
      m_PendingDeals.Remove(placedOrder);
      placedOrder.OnPlaced-=this.OrderPlaced;
}

Listing 3

The last line unsubscribes the event and removes the strong reference. If this is news to you, then drop everything and look at all your event handling code. Chances are you have a leak.

Databinding (WPF + Silverlight, All versions)

You read that right; data binding, the thing you rely on, can cause memory leaks. Strictly speaking it’s actually the way you use it that causes the leak and, once you know about it, it’s easy to either avoid or code around this issue.

The Issue

If you have a child object that data binds to a property of its parent, a memory leak can occur. An example of this is shown in Listing 4, below.

<Grid Name="mainGrid">
           <TextBlock Name=”txtMainText” Text="{Binding ElementName=mainGrid, Path=Children.Count}" />
</Grid>

Listing 4: DataBinding Leak Example

In this example, the condition will only occur if the bound property is a PropertyDescriptor property, as Children.Count is. This is because, in order to detect when a PropertyDescriptor property changes, the framework has to subscribe to the ValueChanged event, which in turn sets up a strong reference chain.

If the binding is marked as OneTime, the bound property is a DependencyProperty, or the object implements INotifyPropertyChanged, then the issue won’t occur. In the case of OneTime binding this is because, as the name suggests, it doesn’t need to detect property changes, as the binding occurs from data source to consumer just once.

Solution

There are a number of work-arounds for this problem.
  1. Add a DependencyProperty [to the page/window] which simply returns the value of the required PropertyDescriptor property. Binding to this property instead will solve the problem..
  2. Make the binding OneTime

    Text="{Binding Path=Salary, Mode=OneTime}"/

    Listing 5
  3. Add the following line of code on exit from the page:
    BindingOperations.ClearBinding(txtMainText, TextBlock.TextProperty);

    Listing 6
    (This simply clears the binding and removes the reference.)

Static events (WPF + Silverlight, All versions)

Subscribing to an event on a static object will set up a strong reference to any objects handling that event. Statics are a classic source of root references, and are responsible for a high proportion of leaks in code.
Statics, once referenced, remain for the duration of the app domain execution, and therefore so do all their references. Strong references preventing garbage collection are just memory leaks by another name.
To show you what I mean, the code below subscribes the calling class to the event source, EventToLeak, on the static object MyStaticClass:

MyStaticClass.EventToLeak += new EventHandler(AnEvent);

Listing 7

The handling event, AnEvent, will be called when the EventToLeak event fires:

protected override void AnEvent(EventArgs e)
{

  // Do Soemething

}
Listing 8

If you don’t subsequently unsubscribe the event, then it will leak because MyStaticClass continues to hold a strong reference to the calling class.

The Solution

To unsubscribe, simply add the code line:

MyStaticClass.EventToLeak -= this.AnEvent;
This releases the strong reference from MyStaticClass. It’s a simple solution, but then it’s a simple problem – human error and oversight.

Command Binding

Command binding is a really useful feature in WPF; it allows you to separate common application commands and their invocation (such as Cut, Paste, etc ) from where they are handled. You can write your classes to handle specific commands, or not, and even indicate if those commands can be executed. As useful as these bindings are, you do have to be careful about how you use them.

The Issue

In the following example I am setting up some code within a child window to handle when Cut is executed within the parent mainWindow. I first create a CommandBinding, and then simply add it to the parent window’s CommandBindings collection.

CommandBinding cutCmdBinding = new CommandBinding(ApplicationCommands.Cut, OnMyCutHandler, OnCanICut);            
 mainWindow.main.CommandBindings.Add(cutCmdBinding);


…..
void OnMyCutHandler (object target, ExecutedRoutedEventArgs e)
{
   
    MessageBox.Show("You attempted to CUT");
}


void OnCanICut (object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = true;
}

Listing 9

You may be able to see what the problem is just from reading the code above because, at the moment, it leaks. It’s because we are leaving a strong reference in the mainWindow.main.CommandBindings object, pointing to the child. As a result, even when the child closes, it will still remain in memory due to the held reference.

This is obviously a contrived example to illustrate the point, but you can easily set this scenario up without even realizing it.

The Solution

Again, the solution couldn’t be easier and, not surprisingly, involves removing the command binding reference:

mainWindow.main.CommandBindings.Remove(cutCmdBinding);
Listing 10
Once this reference is removed, the leak will go away.

DispatcherTimer Leak

Improper use of the DispatcherTimer will cause a memory leak. There’s not much more background to this, so let’s just jump right in.

The problem

The code below creates a new DispatcherTimer within a user control. A textbox is updated with the contents of the count variable, which is updated every second by the DispatcherTimer. To make it easier to see the leak, I have also added a byte array called myMemory, which just makes the leak much bigger and easier to see.

        public byte[] myMemory = new byte[50 * 1024 * 1024];
       
        System.Windows.Threading.DispatcherTimer _timer = new System.Windows.Threading.DispatcherTimer();
        int count = 0;
       

        private void MyLabel_Loaded(object sender, RoutedEventArgs e)
        {

            _timer.Interval = TimeSpan.FromMilliseconds(1000);

            _timer.Tick += new EventHandler(delegate(object s, EventArgs ev)

            {
                count++;
                textBox1.Text = count.ToString();
            });

            _timer.Start();
        }

Listing 11

On my main window, I am adding an instance of the UserControl to a StackPanel (after removing children first) on a button click. This will leak memory for every button click and, as mentioned a moment ago, in this example the main leak you will see is the byte array. Tracing it backwards (using ANTS Memory Profiler in this case, though any profiling tool will do) shows that the UserControl as the source of the leak.

This probably feels familiar, as the problem is once again a reference being held, this time by the Dispatcher, which holds a collection of live DispatcherTimers. The strong reference from the collection keeps each UserControl alive, and therefore leaks memory.

The Solution

The solution is really simple but easy to forget and, you guessed it, you’ve got to stop the timer and set it to null. Here’s the code to do that:
_timer.Stop();
_timer=null;
Listing 12

TextBox Undo Leak

The last leak I want to draw your attention to is not really a leak; it is intended behavior, but it’s important to know it’s there.

The Problem

The problem is to do with the TextBox control and UNDO. TextBoxes have built-in undo functionality, enabling a user to undo their changes to the contents of a text box. To achieve that, WPF maintains a stack of recent changes, and when you use a memory profiler, you can clearly see a build up of data on this undo stack.
This isn’t a major problem unless your app is updating large strings to text boxes over many iterations. The main reason to note this behavior is because it can often show up on memory profile traces, and there is often no point being distracted by it.

The solution

You can limit the behavior of the undo stack by either switching it off:
textBox1.IsUndoEnabled=false;
Listing 13

Or alternatively you can reduce its impact by setting the UndoLimit property:

textBox1.UndoLimit=100;

Listing 14

This limits the number of actions that can be undone, in this case to 100. By default the setting is -1, which limits the number of actions only by the amount of memory available. Setting the value to zero also switches undo off.

Conclusion

None of this is rocket science, and it’s all based on the same principle: “leave a reference behind and potentially you have a leak”. Obviously that depends on whether the left reference is ultimately connected to a root reference.

While nothing I have covered is strictly speaking a bug, all of the points are definitely gotchas that you can easily be caught by without realizing it.
I should know, because I see them all again and again in the projects that I work on.

Ultimately, the two things I recommend you do to avoid memory leaks in the future are:
  • Learn all you can about .NET memory management and how your code impacts it
  • Get used to routinely using a memory profiler and interpreting it’s results to trace issues such as the many potential flavors of left behind strong references. 


Post a Comment