使用SharedResourceDictionary时发生内存泄漏 [英] Memory leak when using SharedResourceDictionary

查看:139
本文介绍了使用SharedResourceDictionary时发生内存泄漏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果您使用某些较大的wpf应用程序,则可能对熟悉.因为ResourceDictionaries总是实例化的,所以每次在XAML中找到它们时,我们最终可能会在内存中多次拥有一个资源字典.因此,上述解决方案似乎是一个很好的选择.实际上,对于我们当前的项目,此技巧确实发挥了很多作用……内存消耗从800mb降低到44mb,这确实是一个巨大的影响.不幸的是,这种解决方案需要付出一定的代价,我想在这里展示一下,希望找到一种避免使用它的方法,同时仍然使用SharedResourceDictionary.

if you worked on some larger wpf applications you might be familiar with this. Because ResourceDictionaries are always instantiated, everytime they are found in an XAML we might end up having one resource dictionary multiple times in memory. So the above mentioned solution seems like a very good alternative. In fact for our current project this trick did a lot ... Memory consumption from 800mb down to 44mb, which is a really huge impact. Unfortunately this solution comes at a cost, which i would like to show here, and hopefully find a way to avoid it while still use the SharedResourceDictionary.

我举了一个小例子来展示共享资源字典中的问题.

I made a small example to visualize the problem with a shared resource dictionary.

只需创建一个简单的WPF应用程序.添加一个资源Xaml

Just create a simple WPF Application. Add one resource 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>

现在添加一个UserControl.后面的代码只是默认代码,所以我只显示xaml

Now add a UserControl. The codebehind is just the default, so i just show the 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>

后面的窗口代码看起来像这样

The Window code behind looks something like this

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

And the window xaml like this

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>

我知道该程序并不完美,可以简化该程序,但是想出一种方法来显示问题,这就是我想出的.无论如何:

I know the program is not perfect and propably could be made easier but while figuring out a way to show the problem this is what i came up with. Anyway:

启动此操作,然后检查内存消耗,如果您有内存分析器,这将变得更加容易.添加(通过单击选项卡显示它)并删除页面,您将看到一切正常.没有泄漏. 现在,在UserControl.Resources部分中,使用SharedResourceDictionary而不是ResourceDictionary来包含 Shared.xaml .您会看到MyUserControl将在删除页面及其后的页面后保留在内存中.

Start this and you check the memory consumption, if you have a memory profiler this becomes much easier. Add (with showing it by clicking on the tab) and remove a page and you will see everything works fine. Nothing leaks. Now in the UserControl.Resources section use the SharedResourceDictionary instead of the ResourceDictionary to include the Shared.xaml. You will see that the MyUserControl will be kept in memory after you removed a page, and the MyUserControl in it.

我认为这种情况发生在通过XAML实例化的所有内容(如转换器,用户控件等)上.奇怪的是,自定义控件不会发生这种情况.我的猜测是,自定义控件,数据模板等都没有真正实例化.

I figured this happens to everything that is instantiated via XAML like converters, user controls etc. Strangely this won't happen to Custom controls. My guess is, because nothing is really instantiated on custom controls, data templates and so on.

那么首先我们如何避免这种情况?在我们的情况下,必须使用SharedResourceDictionary,但是内存泄漏使它无法有效地使用它. 可以使用CustomControls代替UserControls来避免泄漏,这在实际中并不总是如此.那么为什么ResourceDictionary强烈引用UserControls? 我想知道为什么以前没有人经历过,就像我在一个较早的问题中所说的那样,看来我们使用资源字典和XAML绝对是错误的,否则我想知道为什么它们如此无效.

So first how we can avoid that? In our case using SharedResourceDictionary is a must, but the memory leaks makes it impossible to use it productively. The Leak can be avoided using CustomControls instead of UserControls, which is not always practically. So why are UserControls strong referenced by a ResourceDictionary? I wonder why nobody experienced this before, like i said in an older question, it seems like we use resource dictionaries and XAML absolutely wrong, otherwise i wonder why they are so inefficent.

我希望有人能对此事有所启发.

I hope somebody can shed some light on this matter.

先谢谢了 尼科

推荐答案

我不确定这是否可以解决您的问题.但是我在ResourceDictionary引用控件以及与懒惰水合作用.这是帖子就可以了.这段代码解决了我的问题:

I am not quite sure if this will solve your issue. But I had similar issues with ResourceDictionary referencing controls and its to do with lazy hydration. Here is a post on it. And this code resolved my issues:

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        WalkDictionary(this.Resources);

        base.OnStartup(e);
    }

    private static void WalkDictionary(ResourceDictionary resources)
    {
        foreach (DictionaryEntry entry in resources)
        {
        }

        foreach (ResourceDictionary rd in resources.MergedDictionaries)
            WalkDictionary(rd);
    }
}

这篇关于使用SharedResourceDictionary时发生内存泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆