Silverlight + MVVM + Bindings =内存泄漏?

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

显然这是一个巨大而荒谬的主张 – 所以我的期望是有人会明白我为什么以及在哪里出错:)

重现的步骤很简单:

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

  • 以某种方式利用绑定(例如 – 只需键入文本框)。

这将创建一个从根,BindingExpression扩展到viewmodel的引用链。 然后,您可以从UI树中删除View以及所有对VM的引用 – 但是由于root BindingExpression VM引用链,VM永远不会被垃圾回收。

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

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

例1

例2

对于那些可能想要帮助但又不想下载示例项目的人,这里是代码2的代码。我们从一个名为FooViewModel的viewmodel开始:

  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,包含:

       

(为简洁省略了名称空间)

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

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

MainPage看起来像这样:

  

在后面的代码中有以下内容:

  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工作站。

这个问题似乎没有发生在我试过的其他机器上 – 包括办公室中的几台家用电脑和其他电脑。 我们在某种程度上排除了SL版本,内存量以及vmware。 还没有找到原因。

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

  • 平板电脑输入服务(换句话说,所有’平板电脑’个人电脑)
  • 自动化测试工具
  • 屏幕阅读器(和其他可访问性软件)

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

因此,一个新问题浮出水面:我们如何禁用自动化操作器或以其他方式正确清理它们?

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

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

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

编辑:

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

  

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

第二个示例中没有内存泄漏。

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

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

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