Silverlight + MVVM +绑定=内存泄漏?

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

显然这是一个巨大而荒谬的说法 - 所以我的期望是有人会明确回答我为什么和在哪里会出错:)

重现步骤很简单:

  • 通过将视图datacontext设置为虚拟机(假设视图模型利用INotifyPropertyChanged支持数据绑定),将视图模型绑定到视图。
  • 将UI元素绑定到视图模型上的属性,例如:
  • <TextBox Text="{Binding SomeText}" />

  • 以某种方式利用绑定(例如 - 只需输入文本框)。
  • 这将创建一个引用链,它从根,扩展到BindingExpression,到您的视图模型。 然后,您可以从UI树中移除View,以及所有对VM的引用 - 但是,由于根目录<> BindingExpression <> VM引用链,VM永远不会被垃圾收集。

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

    示例1是一个超级剥离校准微型示例。 例2不使用框架,只是用我能想到的最简单的方式来说明问题。

    例1

    例2

    对于那些可能想要帮助但不希望下载示例项目的人员,下面是示例2的代码。我们从名为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是必要的,以促进绑定。

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

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

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

    这里的重要一点是绑定到FooText属性的文本框。 当然,我们需要设置datacontext,我选择在代码隐藏中做,而不是引入ViewModelLocator:

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

    MainPage看起来像这样:

    <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");
        }
    

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

    值得注意的是,这篇知识库文章可能是相关的,但是我不确定,因为'方法2'的解决方法似乎没有效果,并且引用链似乎不匹配。

    另外,我不确定它的重要性,但是我使用CLR Profiler来诊断原因。

    更新:

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

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

    这个问题似乎没有发生在我尝试过的其他机器上 - 包括办公室中的几台家用PC和其他PC。 我们在某种程度上排除了SL版本,内存容量以及可能的vmware。 还没有确定一个原因。


    目前尚未找到解决方案,但问题现在已确定。 如果由于以下原因调用Silverlight的自动化系统,则会发生此行为:

  • 平板电脑输入服务(换句话说,所有'平板电脑'PC')
  • 自动化测试工具
  • 屏幕阅读器(和其他可访问性软件)
  • 更多信息在这里:http://www.wintellect.com/cs/blogs/sloscialo/archive/2011/04/13/silverlight-memory-leaks-and-automationpeers.aspx

    因此,一个新问题将出现:我们应该如何禁用自动化设备或以其他方式正确清理它们?

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

    然而,这不是一个真正可行的解决方案,因为我们必须重写我们打算使用绑定的每个silverlight控件,更不用说复杂控件的控件模板。

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

    编辑:

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

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

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


    你的第二个例子中没有内存泄漏。

    使用myContent.Content = new FooView();影响新的FooView实例到您的ContentControl myContent.Content = new FooView(); ,整个View + ViewModel对象图没有更多的使用引用

    它最终会在需要时被垃圾收集。

    也许你应该澄清一下,你认为是什么让你觉得有内存泄漏(即统计数据,重复步骤等)。

    链接地址: http://www.djcxy.com/p/22793.html

    上一篇: Silverlight + MVVM + Bindings = Memory leaks?

    下一篇: Silverlight not initializing controls