Memory leak with WPF resources (in rare cases)

It seems there is bug in the WPF resource architecture that can cause memory not to be released and causing performance and stability problems. The document describes when the problem occurs and why.

During the development of a WPF application, we noticed that a huge amount of memory was used after a while. After profiling with the excellent .NET Memory Profiler v3.1 we found out that a lot of objects were not collected by the garbage collector. After some heavy investigation I found out that a DropShadowEffect was holding references to objects that should have been garbage collected already.

When does the problem occur?
The problem occurs in the following situation:

  1. A style is defined in the application’s ResourceDictionary.
  2. The style uses a control template that uses media effects (i.e. DropShadowEffect).
  3. The media effect should be referenced using a StaticResource.

Why does the problem occur?
When a media effect is attached to a UIElement, then the UIElement.VisualEffectInternal property setter is invoked by the WPF framework. This method registers to the Effect.Changed event when the media effect is not frozen. This is correct, because a non-frozen effect might change its properties and the UI element should invalidate. A frozen effect cannot be changed, so there is no need to register to the Effect.Changed event.

This behavior is correct and also documented in the MSDN library in the chapter Optimizing Performance: Object Behavior:

Changed Handlers on Unfrozen Freezables may Keep Objects Alive
The delegate that an object passes to a Freezable object’s Changed event is effectively a reference to that object. Therefore, Changed event handlers can keep objects alive longer than expected. When performing clean up of an object that has registered to listen to a Freezable object’s Changed event, it is essential to remove that delegate before releasing the object.

WPF also hooks up Changed events internally. For example, all dependency properties which take Freezable as a value will listen to Changed events automatically.

Unfortunately, there seems to be no code in the WPF framework that removes the delegate again. The only way to unsubscribe is to set the Effect to NULL. Using media effects that are not frozen seems to be dangerous.

As long as the media effect is frozen, then the framework doesn’t subscribe to the Changed event and everything is fine. But that’s the problem… When using a merged resource dictionary at the application level, then the resources inside of it are not always frozen (see the example application). If the resource is marked explicitly to be frozen, then it will be frozen and everything works fine.

What kind of puzzled me, is the fact that when the effect and style are defined directly at the “Application.Resources” level (without a resource dictionary), then the effect is frozen by default. If they are defined in a resource dictionary that is merged inside the “Application.Resources” then the effect is not frozen! You can force the effect to be frozen by specifying “PresentationOptions:Freeze=True”, but this is not common behavior. So, putting resources into resource dictionaries has side effects that are not foreseen and are not (clearly) documented in the WPF documentation.

Because the media effect is shared and the delegates are not removed when the UI element is not needed anymore it keeps the UI elements alive. WPF holds a strong hierarchy in objects, so a lot of other objects stay in memory too, resulting in a lot of wasted memory that can never be regained anymore.

The actual problem is that effects that are defined in a resource dictionary that is loaded from the Application object are not frozen in some cases. I haven’t done a thorough investigation why this doesn’t happen, but I think this is a bug in the WPF framework. The framework has a lot of places where it checks if a resource is of a certain type and if it is, then it should be sealed or frozen. I wouldn’t be surprised that the framework misses such a check for media effects, because they have been added later (in v3.5 SP1). Of course, this statement is pure speculation.

The workaround is simple… Just add the Freeze attribute to all the effects that you don’t plan to modify at runtime.

Sample application
I have created a sample application that illustrates the problem. When the application is started, then you can see a checkbox that has a dropshadow effect applied to it. The window also shows whether or not this effect is frozen.

As you can see the effect is not frozen, when the effect and style are defined in the resource dictionary. If you comment out the resource dictionaries from App.xaml and uncomment the effect and style resources in App.xaml, then the application looks just the same, but you’ll see that the effect is now frozen.

The application doesn’t illustrate the actual memory leak, but in a real-life application (like ours) it actually has memory leaks that can become quite large.

Conclusion
I guess Microsoft introduced a problem when using effect and styles in resource dictionaries. Because I didn’t investigate why it wasn’t frozen, I cannot determine whether this is a real bug or just a side-effect of some design choice.

I also understand that it might be a bad idea to define resources at the application level this way. Using themes is probably a better solution and might solve this issue. But, there are just people out there using this approach and they don’t expect this behavior. The fix is simple… Just freeze all media effects that you don’t plan to change at runtime, which helps us to fix this for our next release.

Sample application
Download the sample application.

Memory leak with WPF animations

It seems there is bug in the WPF animation architecture that can cause memory not to be released and causing performance and stability problems. The document describes when the problem occurs and why.

During the development of a WPF application, we noticed that a huge amount of memory was used after a while. After profiling with the excellent .NET Memory Profiler v3.1 we found out that a lot of objects were not collected by the garbage collector.

After some heavy investigation I found out that a FrameworkTemplate was holding references to a lot of my textboxes that should have been collected already. These textboxes held references to other objects, so the result was that a lot of memory was wasted. When the event triggers with actions were removed from the textbox’s control template, then the memory was released properly.

When does the problem occur?
The problem occurs in the following situation:

  1. The control has a control template defined.
  2. The control template uses triggers with enter/leave actions.
  3. The control has the ‘Collapsed’ visibility.

Why does the problem occur?
Triggers are active during the initialization of a control. If a property triggers an enter/leave action, then the action is started. The action is started via the internal “StyleHelper.InvokeActions” method. This method checks if the visual tree of the object is already constructed and if it is, then the actions are invoked immediately.

If the visual tree hasn’t been created yet, then the action is queued by the “StyleHelper.DeferActions”, so it can be processed later. To my surprise, the deferred actions are stored with the template and not the control itself (IMHO: This is a fundamental design flaw). If the visual tree is created, then the deferred actions will be invoked.

The visual tree is created during the public “FrameworkElement.ApplyTemplate” method. The framework only calls this method when measuring the control. So if the control is never measured, then the visual tree is not created and the deferred actions will remain queued forever.

The MSDN documentation for “FrameworkElement.MeasureOverride” clearly states:

Important Note:
Elements should call Measure on each child during this process, otherwise the child elements will not be correctly sized or arranged.

You might expect that all elements call “FrameworkElement.Measure” on their children, so during this process the “FrameworkElement.ApplyTemplate” is called. However, WPF doesn’t respect this rule, because “UIElement.Measure” doesn’t start measuring when the element has a collapsed visibility.

Collapsed UI elements are never measured and if they never will get the Visible or Hidden visibility, then the object is never measured and the template isn’t applied. This seems very logical, but it also causes the deferred actions never to be removed from the template.

Because the template keeps the reference to the control, the control is never garbage collected.  WPF controls know their parents and children. They also reference other objects via databinding. The result is that a lot of objects are referenced by the control (direct or indirect) and are not garbage collected.

Sample application
I have created a sample application that illustrates the problem. When the application is started, then you can see the deferred actions for the control template of the MySampleControl.

When you press the “Create Collapsed Expander”, the application creates a window that contains a collapsed expander that contains a MySampleControl. Because the MySampleControl is collapsed it isn’t constructed and you can see the control is added to the deferred actions list (after refreshing the list).

When you expand the expander on the window and refresh the deferred actions, then you can see that the control is removed from the list. If you close the window, without expanding the expander, then the control will stay on the list forever. Because the item is still on the list it is never collected.

You can force a garbage collection and in the debug window (when running in Visual Studio) you can see which objects are finalized. Windows with expanders that have never been opened will never be finalized.

Conclusion
I guess Microsoft made a design error by storing the deferred actions with the template. They should have been stored in the control itself. The deferred actions are only used when applying the template, which is an instance method of the control. So it would have had full access. Now a dictionary is used that connects objects that shouldn’t be connected.

Response from Microsoft
I have received a response from Microsoft and they confirm it is a bug. It is on the list to be fixed, but there is no releasedate for .NET 3.5 SP2 or .NET 4. So I guess we’ll have to wait.

Microsoft .NET Framework 4
Beta 1 of the Microsoft .NET Framework 4 has been released and it now uses a ConditionalWeakTable instead of a HybridDictionary. The keys are kept as weak references and when the key is collected by the GC, then the associated value is also released. This also solves this issue in our situation, because the dictionary doesn’t keep the objects alive anymore.

Sample application
Download the sample application.