I was not alone in this situation and I found out that there were both a work-around and an hotfix available. Here is the information I gathered.

 

Description of the problem

When you create and load a ResourceDictionary, the objects you add inside are actually not created immediately. They are referenced in the form of an instance of an object called DeferredResourceReference. This instance represents the actual object in the ResourceDictionary in a dehydrated form. Then when the object is really needed, it is inflated (i.e. hydrated) and so really instantiated. In this process it raises an event named Inflated. By following this process, the resource are only loaded when they are really needed. It is lazy and as developers, we love it !

When you link a property in the XAML using a DynamicResource it will in fact returns an object of type ResourceReferenceExpression. This object represents the resource not necessary loaded. If the pointed resource is already loaded(hydrated), it will returns it directly. If not it will returns DependencyProperty.UnsetValue and register a callback to the corresponding Inflated event(remember: the object is not stored directly in the resource dictionary but on the form of a DeferredResourceReference).

 

In the callback of the Inflated event, it will unsubscribe of it and retrieve the hydrated value originally wanted. Also it will subscribe to the Changed event if it is a Freezable object not already freezed to follow its changes. This is nice and this is how dynamic resources works: you can change them dynamically, the binded properties will automatically updated themselves.

 

The problem is that if the object in the resource dictionary is not hydrated, the event Inflated will never get raised and the DynamicResource will never unsubscribe from this event. This can happen for example if the dynamic resource is used in a trigger which is never triggered or in a control never placed in a visible visual tree.

It will so create a link from the resource dictionary to the final UI where the dynamic resource is:

  1. The Application store the resource dictionary(RD),
  2. The RD hold a reference to the DeferredResourceReference(DRR),
  3. The ResourceReferenceExpression(RRE) hold a reference to the DRR via a subscription to its Inflated event,
  4. And finally your view hold a reference to the RRE because it was provided to it by the DynamicResource markup.

 

Here is the reference graph created using Ant memory profiler:

 

MemoryLeakDynamicResourceWPF3.5

 

 

As a resource dictionary is often hold alive the whole application life, the linked UI won’t be available for garbage collection the whole application life too. A memory leak is just born in front of us: too sad !

 

How to solve this issue

The WPF team noticed this problem and they fixed it for the 4.0 version. If you are still using the 3.5 (event the SP1) version of WPF, there is an hotfix(KB967328) that you can ask to the Microsoft Customer Support Services.

If you are willing to solve it yourself, it exists a work around as pointed out by “Andrew S” on this MSDN forum. As I told before, if the resource is already hydrated, no subscription to the Inflated event is done and there is no memory leak. So, if we make sure that every resource is loaded before they are actually referenced, we will assure ourselves that no memory leak can happen. Here is the method to load effectively each resource in a resource dictionary:

private static void WalkDictionary(ResourceDictionary resources)
{
foreach (DictionaryEntry entry in resources) { }
foreach (ResourceDictionary rd in resources.MergedDictionaries)
WalkDictionary(rd);
}

 

This have to be done in the Application startup or before adding a resource dictionary to any resources. There is several drawbacks to this solution:

  • It consumes CPU time on startup to walk trough every resources,
  • If you have an application with a lot of styles and resources, each will be loaded in memory even if it is not used. This will so consumes more memory…
  • If a static resource is not resolved, it will raise an exception in this loop. (I am not sure this is really a drawback, I’d better call it a broken-resource-link detector).

 

 

Note: in WPF 4.0, this issue is solved using weak references.

 

Interesting links

Here are some links which may interest you:


Shout it kick it on DotNetKicks.com