Silverlight + MVVM + 绑定 = 内存泄漏? [英] Silverlight + MVVM + Bindings = Memory leaks?

查看:18
本文介绍了Silverlight + MVVM + 绑定 = 内存泄漏?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

到目前为止,我的测试表明,在 Silverlight 中利用 MVVM 模式的所有标准方法、示例和框架都存在一个巨大的问题:大量内存泄漏会阻止 VM 被垃圾收集.

Thus far, my testing has shown that all standard approaches, examples, and frameworks leveraging the MVVM pattern in silverlight suffer from a huge problem: massive memory leaks which prevent VMs from being garbage collected.

显然这是一个巨大而荒谬的声明 - 所以我的期望是有人会对我出错的原因和地方有一个明显的答案:)

Obviously this is a huge and ridiculous claim - so my expectation is that someone will have an obvious answer of why and where I'm going wrong :)

重现的步骤很简单:

  • 通过将视图数据上下文设置为 VM 将视图模型绑定到视图(假设视图模型利用 INotifyPropertyChanged 来支持数据绑定)
  • 将 UI 元素绑定到视图模型上的属性,例如:

<TextBox Text="{Binding SomeText}"/>

  • 以某种方式利用绑定(例如 - 只需在文本框中输入).

这会创建一个引用链,从根扩展到 BindingExpression,再到您的视图模型.然后,您可以从 UI 树中删除视图,以及对 VM 的所有引用——但是,由于根<>BindingExpression<>VM 引用链,VM 永远不会被垃圾收集.

This creates a reference chain that extends from the root, to a BindingExpression, to your viewmodel. You can then remove the View from your UI tree, as well as all refernences to the VM - however the VM will never be garbage collected thanks to the root<>BindingExpression<>VM reference chain.

我创建了两个示例来说明问题.他们有一个按钮来创建一个新的视图/视图模型(它应该转储对旧视图的所有引用)和一个强制垃圾收集和报告当前内存使用情况的按钮.

I have created two examples illustrating the problem. They have a button to create a new view/viewmodel (which should dump all references to the old one(s)) and a button which forces garbage collection and reports on the current memory usage.

示例 1 是一个超级精简的 caliburn 微型示例.示例 2 没有使用框架,只是以我能想到的最简单的方式说明了问题.

Example 1 is a super stripped down caliburn micro example. Example 2 uses no frameworks and just illustrates the problem in the simplest way I could think of.

示例 1

示例 2

对于那些可能想提供帮助但不想下载示例项目的人,这里是示例 2 的代码.我们从一个名为 FooViewModel 的视图模型开始:

For those who might like to help but don't wish to download the example projects, here is the code for example 2. We start with a viewmodel called FooViewModel:

 public class FooViewModel : INotifyPropertyChanged
{
    string _fooText;

    public string FooText
    {
        get { return _fooText; }
        set
        {
            _fooText = value;
            NotifyPropertyChanged("FooText");
        }
    }

    private byte[] _data;
    public FooViewModel()
    {
        _data = new byte[10485760]; //use up 10mb of memory
    }



    private void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

它只是公开了一个名为 FooText 的字符串属性,我们也将绑定它.INotifyPropertyChanged 是必要的,以方便绑定.

It simply exposes a string property called FooText which we will bind too. INotifyPropertyChanged is neccessary to facilitate the binding.

然后我们有一个名为 FooView 的视图,它是一个包含以下内容的用户控件:

Then we have a view called FooView which is a usercontrol containing:

<UserControl x:Class="MVVMLeak.FooView">
    <StackPanel x:Name="LayoutRoot" Orientation="Horizontal">
        <TextBlock Text="Bound textbox: " />
        <TextBox Text="{Binding FooText}" Width="100"/>
    </StackPanel>
</UserControl>

(为简洁起见省略了命名空间)

(namespaces omitted for brevity)

这里的重要一点是绑定到 FooText 属性的文本框.当然,我们需要设置数据上下文,我选择在代码隐藏中进行而不是引入 ViewModelLocator:

The important bit here is the textbox which is bound to the FooText property. Of course we need to set the datacontext, which I've chosen to do in the codebehind rather than introduce a ViewModelLocator:

public partial class FooView : UserControl
{
    public FooView()
    {
        InitializeComponent();
        this.DataContext = new FooViewModel();
    }
}

主页看起来像这样:

<StackPanel x:Name="LayoutRoot" Background="White">

    <Button Click="Button_Click" Content="Click for new FooView"/>
    <Button Click="Button2_Click" Content="Click to garbage collect"/>
    <ContentControl x:Name="myContent"></ContentControl>
</StackPanel>

后面的代码如下:

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        myContent.Content = new FooView();
    }

    private void Button2_Click(object sender, RoutedEventArgs e)
    {
        MessageBox.Show("Memory in use after collection: " + (GC.GetTotalMemory(true) / 1024 / 1024).ToString() + "MB");
    }

注意:要重现问题,请务必在文本框中键入内容,因为我相信绑定表达式在需要时才会创建.

Note: To replicate the problem, be sure to type something in the textbox, as I believe the Binding Expression isn't created until it's needed.

值得注意的是,这篇知识库文章可能是相关的,但是我'我不相信,因为方法 2"解决方法似乎没有效果,而且参考链似乎不匹配.

It's worth noting that this KB article may be related, however I'm not convinced since 'method 2' workaround doesn't seem to have an effect, and the reference chain doesn't seem to match.

另外,我不确定这是否重要,但我使用了 CLR Profiler 来诊断原因.

Also, I'm not sure it matters, but I used CLR Profiler to diagnose the cause.

更新:

如果有人想测试并在评论中报告他们的发现,我将通过 Dropbox 托管 Silverlight 应用程序:托管示例.重现:点击顶部按钮,输入内容,点击顶部按钮,输入内容,点击顶部按钮.然后按下按钮.如果它报告 10MB 的使用量(或者可能是其他一些没有增加的量),则您没有遇到内存泄漏.

If anyone would like to test, and report their findings in a comment, I'm hosting the silverlight application via dropbox here: Hosted Example . To reproduce: Hit the top botton, type something, hit the top button, type something, hit the top button. Then hit the button. If it reports 10MB usage (or perhaps some other amount that is not increasing), you are not experiencing the memory leak.

到目前为止,问题似乎发生在我们所有的开发机器上,这些机器是 ThinkPad w510 (43192RU) 和 12GB 内存,64 位 Win 7 Enterprise.这包括一些没有安装开发工具的.值得注意的是,他们正在运行 VMWare 工作站.

Thus far, the problem seems to be happening on ALL of our development machines, which are ThinkPad w510 (43192RU) with 12GB Ram, 64 bit Win 7 Enterprise. This includes some which have not had development tools installed. It might be worth noting that they are running VMWare workstation.

在我尝试过的其他机器上似乎没有发生该问题 - 包括几台家用 PC 和办公室中的其他 PC.我们在某种程度上排除了 SL 版本、内存量和 vmware.仍然没有确定原因.

The problem does NOT seem to happen on other machines I have tried - including several home PCs and other PCs in the office. We have somewhat ruled out SL versions, amount of memory, and probably vmware. Still haven't nailed down a cause.

推荐答案

尚未找到解决方案,但问题现已确定.如果由于以下原因调用 Silverlights 的自动化功能,则会发生此行为:

A solution is yet to be found, however the problem is now identified. This behavior will occur if Silverlights' automation faculties are invoked due to:

  • 平板电脑输入服务(换句话说,所有类似平板电脑"的电脑)
  • 自动化测试工具
  • 屏幕阅读器(和其他无障碍软件)

此处的更多信息:http://www.wintellect.com/cs/blogs/sloscialo/archive/2011/04/13/silverlight-memory-leaks-and-automationpeers.aspx

因此出现了一个新问题:我们如何禁用自动化对等节点或以其他方式让它们正确清理?

So a new problem surfaces: How do we disable automationpeers or otherwise get them to clean-up correctly?

这篇文章说明了一种方法:WPF UserControl 内存泄漏

This post illustrates one approach: WPF UserControl Memory leak

然而,这并不是一个真正可行的解决方案,因为我们必须覆盖我们计划使用绑定的每个 Silverlight 控件,更不用说复杂控件的控件模板了.

However, it isn't really a viable solution as we'd have to override every silverlight control which we plan to use binding for, not to mention the control templates of complex controls.

如果有人能找到一个好的解决方案,我会改变我的答案,但现在似乎没有一个......

I will change my answer if anyone can identify a good solution, but for now there doesn't seem to be one...

这是一个很好的小解决方法,它似乎可以完成这项工作.只需在您定义 Silverlight 对象的 HTML 中添加以下参数:

Here is a nice little workaround which seems to do the job. Simply add the following parameter in your HTML where you define the silverlight object:

<param name="windowless" value="true" />

在无窗口"模式下运行的副作用是自动化不起作用:)

A side-effect of running in 'windowless' mode is that automation doesn't work :)

这篇关于Silverlight + MVVM + 绑定 = 内存泄漏?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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