Wednesday, September 30, 2009

WPF Memory Leaks

Using of Event Handlers

Event handlers are a very common source of non-obvious memory leaks. For example, you think an event handler on an object is getting disposed when the object is destroyed, unaware that the object is not being released from memory, because the event handler is continued to be used elsewhere.

If you subscribe to an event on object1 from object2, then dispose of object2 and assume it no longer exists (and drop out all references from your code); there is an implicit reference in object1's event that will prevent object2 from being garbage collected.

Code:

SomeObject object1 = new SomeObject();
OtherObject object2 = new OtherObject();

object1.SomeEvent += object2.myEventHandler;
object2 = null;

The Fix/Work around:

object1.SomeEvent -= object2.myEventHandler;

This is a common case of a leak; forgetting to unsubscribe from events. If you create an object that attaches an event handler, then this object must remove that event handler again (at an appropriate time).

Using of Static Variables

This is an example of creating a reference which is allocated to static variable, and by nature, static variables are not destroyed. By not setting the static variable back to null; this will cause the object to reference the static variable, and stop the garbage collector from disposing of the object.

The static staticObject reference is always reachable from code; the garbage collector would not be able to destroy the allocated object.

Code:

static SomeStaticClass staticObject;

static void SomeStaticMethod()
{
staticObject = new SomeStaticClass();
}

The Fix/Work around:

staticObject = null;

This is a common case of a leaking; forgetting to set an allocated object to null.

Using of DynamicResource

It seems that using a DynamicResource to refer to a global resource can cause memory leaks to occur.

• StaticResource - "look up the resource once, then just keep using the same value."
• DynamicResource - "look up the resource each time it's needed, in case the value has changed."

Code:

Background="{DynamicResource Style}"

The Fix/Work around:

Background="{StaticResource Style}"

Avoid using DynamicResource, use StaticResource to access resources unless you absolutely have to use a DynamicResource.

Using of RegisterClassCommandBinding/RegisterClassInputBinding

You can register a class command binding for a command, using CommandManager.RegisterClassCommandBinding or you can register a class input binding for an input, using CommandManager.RegisterClassInputBinding.

However there is an internal bug with these two methods on the CommandManager class, which can cause memory leaks to occur - the garbage collector cannot unregister the binding.

Code:

CommandManager.RegisterClassCommandBinding(type, command binding);

The Fix/Work around:

this.CommandBindings.Add(command binding);

Avoid using CommandManager and class command bindings; instead bind the command to the current instance object. As the object is destroyed the command will now be destroyed with it.

Using of BitmapEffects

Up until .Net 3.5 SP1, all BitmapEffects were rendered in software, which causes memory leak.

New Effects (now called BlurEffect, DropShadowEffect) are introduced as of .Net 3.5 SP1, which are hardware accelerated and rendered by the GPU, which will stop the memory leak (as claimed by Microsoft).

Code:

<Border.BitmapEffect>
<DropShadowBitmapEffect ShadowDepth="10" Softness="1" />
</Border.BitmapEffect>

The Fix/Work around (from .NET 3.5 SP1):

<Border.Effect>
<DropShadowBitmapEffect ShadowDepth="10" Softness="1" />
</Border.Effect>

As of .Net 3.5 SP1, you can use the new Effects classes instead. You must avoid using old Effects, i.e. BevelBitmapEffect, BlurBitmapEffect, DropShadowBitmapEffect, BevelBitmapEffect, EmbossBitmapEffect, OuterGlowBitmapEffect as these are now obsolete classes.

Using of DataBinding

Extract from Microsoft Bug Report:

Data binding operation that is not marked as OneTime must listen for property change notifications from the source object. WPF uses the built-in notifications of the DependencyProperties class or the notifications from the INotifyPropertyChanged interface.

If the DependencyProperties class and the INotifyPropertyChanged interface are unavailable, WPF uses the ValueChanged event. This behavior involves calling the PropertyDescriptor.AddValueChanged method on the PropertyDescriptor object that corresponds to property.
Unfortunately, this action causes the CLR to create a strong reference from this PropertyDescriptor object to source object. The CLR also keeps a reference to the PropertyDescriptor object in a global table. This behavior causes a reference chain to occur.

As long as the data binding target is used, the binding must continue to listen for changes. This behavior keeps the reference alive between the PropertyDescriptor object and source object, and the target remains in use. This behavior causes a memory leak in source object and in every object to which source object refers. These objects include the data binding target.


Code:

View:
<CollectionViewSource x:Key="Results"
Source="{Binding Path=Data.SomeEntity}">

Entity:
public class SomeEntity
{
// Entity properties
}

The Fix/Work around:

View:
<CollectionViewSource x:Key="Results"
Source="{Binding Path=Data.SomeEntity}">

Entity:
public class SomeEntity : INotifyPropertyChanged
{
// Entity properties
}

You must implement the INotifyPropertyChanged interface on the entity that you are binding to in the view. By inheriting the from the BusinessBase class, you will implement the INotifyPropertyChanged interface.

Using of Generic List to Data Bind

The use of generic list on an entity, then data binding the generic list to the view; it cause a memory leak.
Same problem as the previous example, as the generic list doesn’t implement the INotifyPropertyChanged interface.

Code:

public class SomeEntity : INotifyPropertyChanged
{
// Entity properties
List<User> Users { get; set; }
}

The Fix/Work around:

public class UserCollection : ObservableCollection<User>
{
}

public class SomeEntity : INotifyPropertyChanged
{
// Entity properties
UserCollection Users { get; set; }
}

It is recommended that you use ObservableCollection class instead of the generic list class as it implements the INotifyPropertyChanged interface, and provides many additional benefits. You can then bind the collection to the view without causing a memory leak.

Please let me know if this helps you.

4 comments:

Anonymous said...

Hi Huy.

great Article, just wanted to point out a small typo


<Border.BitmapEffect>
<DropShadowBitmapEffect ShadowDepth="10"
Softness="1"/>
</Border.BitmapEffect>

should be
<Border.BitmapEffect>
<DropBitmapEffect ShadowDepth="10"
Softness="1"/></Border.BitmapEffect>

Thanks,
Gary.

Anonymous said...

Good job, helped a lot with our problems.

Rocky Racoon said...

good lord! that was a darn memory leak indeed (the bitmap effects).

Cost me about 2 days to pinpoint the whole problem on on silly line of XAML.

I was doing heavy repetitive databinding with a dropshadow effect on each item in my listbox which made my memory go skyhigh... Using the 'Effect' instead fixed it.

Cool article, thanx!

Anonymous said...

Awesome! Helped really a lot! Cheers,
Christian