I profiled our application for memory leaks recently, and finally get clear understanding for how ResourceDictionary can result in memory leaks or just excessive memory usage.
I can identify three problems:
- MergedDictionaries
- References from Control to ResourceDictionary
- Using DropShadowEffect
MergedDictionaries:
The typical use of MergedDictionaries looks like this:
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/X.Styles;component/TextBoxStyle.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
This code suggests that there was a ResourceDictionary with Source /X.Styles;component/TextBoxStyle.xaml defined somewhere. The way ResourceDictionary is implemented, there will be two instances of ResourceDictionary objects created in the memory. The original one, and then the one that is added to the MergedDictionaries collection.
Solution:
The solution to this problem is to create your own implementation of ResourceDictionary and use it instead. One of the examples can be found
here.
Then, our code will look like this:
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<a:CachedResourceDictionary Source="/X.Styles;component/TextBoxStyle.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
References from control to Resource Dictionary
Assuming we implemented this approach, what if we have this code now in our application?
<UserControl A>
<UserControl.Resources>
<ResourceDictionary.MergedDictionaries>
<a:CachedResourceDictionary Source="/Actsoft.Styles;component/TextBoxStyle.xaml"/>
</ResourceDictionary.MergedDictionaries>
...
</UserControl.Resources>
...
</UserControl>
We should be fine now don't you think? Apparently not. The problem is that every ResourceDictionary keeps reference to what is called owner. Or even multiple owners. For example, when we add ResourceDictionary to UserControl, this user control becomes the owner of this ResourceDictionary. If we load ResourceDictionary to the application, the application becomes the owner. Not only that, when we add ResourceDictionary to the MergedDictionaries collection, all owners form parent dictionary added to the list of owners for child dictionary. In example above, if we have TextBoxStyle.xaml loaded to the application resources, and then to the UserControl resources, we create references from application to the instance of UserControl. As result, UserControl will remain in heap indefinitely and will not be disposed by garbage collector. And we created this problem by using CachedResourceDictionary, since if we used regular ResourceDictionary, we would have separate copy, which could be disposed as part of UserControl.
Solution:
If you decide to use CachedResourceDictionary, create and remove your user controls dynamically and want them to be disposed, you have to choose how you use resource dictionaries
- Either use them in application resources only and don't use them in UserControl
- Or, use them in User Controls only and don't use them in Application Resources. Beware if you activate one user control before you deactivate another one.
To simplify it, CachedResourceDictionary are for application resources or static user controls.
Problems with DropShadowEffect
The description of the problem can be found here. The problem will appear if you have code like that:
<ResourceDictionary>
<DropShadowEffect x:Key="key" ... />
And then, this resource dictionary is loaded into
application resources.
Solution:
Most likely, you don't have intentions to modify this effect, so instead, you should make it look like this:
<ResourceDictionary>
<DropShadowEffect x:Key="key"
PresentationOptions:Freeze=True
... />
This will create
frozen effect. As result there will be no events assigned, and no references.