时,如果你的工作在一些较大的WPF应用程序,你可能熟悉this。因为ResourceDictionaries总是被实例化,所以每次在XAML中发现它们时,我们最终都会在内存中多次使用一个资源字典。所以上述解决方案似乎是一个非常好的选择。实际上,对于我们目前的项目来说,这个技巧做了很多...内存消耗从800mb降至44mb,这是一个非常大的影响。不幸的是,这个解决方案是有代价的,我想在这里展示,并希望找到一种方法来避免它,同时仍然使用SharedResourceDictionary
。内存泄漏使用SharedResourceDictionary
我做了一个小例子与共享资源字典来可视化的问题。
只需创建一个简单的WPF应用程序。添加一个资源的XAML
Shared.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="myBrush" Color="Yellow"/>
</ResourceDictionary>
现在添加一个用户控件。代码隐藏只是默认的,所以我只是显示XAML
MyUserControl.xaml
<UserControl x:Class="Leak.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:SharedResourceDictionary="clr-namespace:Articy.SharedResourceDictionary" Height="128" Width="128">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Leak;component/Shared.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Rectangle Fill="{StaticResource myBrush}"/>
</Grid>
</UserControl>
的窗口后面的代码看起来是这样的
Window1.xaml.cs
// [ ... ]
public Window1()
{
InitializeComponent();
myTabs.ItemsSource = mItems;
}
private ObservableCollection<string> mItems = new ObservableCollection<string>();
private void OnAdd(object aSender, RoutedEventArgs aE)
{
mItems.Add("Test");
}
private void OnRemove(object aSender, RoutedEventArgs aE)
{
mItems.RemoveAt(mItems.Count - 1);
}
和窗口XAML这样
Window1.xaml
<Window.Resources>
<DataTemplate x:Key="myTemplate" DataType="{x:Type System:String}">
<Leak:MyUserControl/>
</DataTemplate>
</Window.Resources>
<Grid>
<DockPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
<Button Content="Add" Click="OnAdd"/>
<Button Content="Remove" Click="OnRemove"/>
</StackPanel>
<TabControl x:Name="myTabs" ContentTemplate="{StaticResource myTemplate}">
</TabControl>
</DockPanel>
</Grid>
</Window>
我知道程序不健全,propably可以作出更容易,但同时想办法,借以说明问题这是我想出了。总之:
启动这一点,你检查内存消耗,如果你有一个内存分析器这变得更加容易。添加(通过点击标签显示它)并删除一个页面,你会看到一切工作正常。没有任何泄漏。 现在在UserControl.Resources
部分使用SharedResourceDictionary
代替ResourceDictionary
到包括Shared.xaml。您将看到在删除页面后,MyUserControl
将保留在内存中,其中的MyUserControl
将保留在内存中。
我想这恰好是通过XAML实例像器应有尽有,用户控件等奇怪的是这不会发生在自定义控件。我的猜测是,因为没有什么是真正的自定义控件,数据模板等实例化。
那么首先我们该如何避免这种情况?在我们的例子中,使用SharedResourceDictionary是必须的,但内存泄漏使得它不可能高效地使用它。 可以使用CustomControls而不是UserControls来避免泄漏,而这并不总是实际。那么为什么UserControls被ResourceDictionary强引用? 我想知道为什么没有人经历过这种情况,就像我在一个较老的问题中所说的那样,似乎我们使用资源字典和XAML绝对是错误的,否则我想知道他们为什么如此无奈。
我希望有人能够对这件事情有所了解。
在此先感谢 尼科
在你的内存设置,是什么对象保持参照的MyUserControl实例? –
我记不得了,但我几乎可以确定它是一个ResourceDictionary,在我的情况下是SharedResourceDictionary。 – dowhilefor