绑定到列表会导致内存泄漏

当我将ListBox的ItemsSource绑定到List时,绑定引擎在控件消失后保持列表元素。 这会导致所有列表元素保留在内存中。 使用ObservalbleCollection时问题就消失了。 为什么会这样?

窗口标记内的xaml

        

代码背后:

 public MainWindow() { InitializeComponent(); DataContext = new ViewModel(); } private void Button_Click(object sender, RoutedEventArgs e) { this.DataContext = null; ContentControl.Content = null; GC.Collect(); GC.WaitForPendingFinalizers(); } 

视图模型

 class ViewModel : INotifyPropertyChanged { //Implementation of INotifyPropertyChanged ... //Introducing ObservableCollection as type resolves the problem private IEnumerable _list = new List { new Person { Name = "one" }, new Person { Name = "two" } }; public IEnumerable List { get { return _list; } set { _list = value; RaisePropertyChanged("List"); } } class Person { public string Name { get; set; } } 

编辑:为了检查人员问题的泄漏,我使用了ANTS和.Net内存分析器。 两者都显示在按下GC按钮后,只有绑定引擎保持对person对象的引用。

啊,你找到了 现在我明白你的意思了。

您将内容设置为null,因此您杀死强制ListBox但仍然将ItemsSource绑定到List,因此ListBox内存未完全释放。

遗憾的是,这是一个众所周知的问题,并且在MSDN上也有很好的记录。

如果您没有绑定到DependencyProperty或实现INotifyPropertyChanged或ObservableCollection的对象,则绑定可能会泄漏内存,并且您必须在完成后取消绑定。

这是因为如果对象不是DependencyProperty或者没有实现INotifyPropertyChanged或者没有实现INotifyCollectionChanged(Normal list没有实现它),那么它通过PropertyDescriptors AddValueChanged方法使用ValueChanged事件。 这会导致CLR从PropertyDescriptor创建一个强引用到对象,并且在大多数情况下,CLR将在全局表中保留对PropertyDescriptor的引用。

因为绑定必须继续监听更改。 当目标保持使用时,此行为使PropertyDescriptor和对象之间的引用保持活动状态。 这可能导致对象中的内存泄漏以及对象引用的任何对象。

问题是……是实施INotifyPropertyChanged的人吗?

我看了你的JustTrace内存分析器的例子,除了一个明显的问题,你为什么要杀死视图模型/ nullify DataContext并让视图运行(在99.9%的情况下你会杀死View和DataContext – 因此ViewModel和Bindings去了范围自动)这是我发现的。

如果您将示例修改为:它将正常工作:

  • 将DataContext替换为新的视图模型实例,正如预期的那样,现有的Person实例超出范围,因为MS.Internal.Data.DataBingingEngine会刷新所有绑定,即使它们是强引用,也不是由WeakPropertyChangedEventManager管理,或者:
  • ViewModel用新的IEnumerable实例替换List,即新的Person [0] /只是null并在ViewModel上引发INCP.PropertyChanged(“List”)

以上修改certificate您可以安全地在绑定中使用IEnumerable / IEnumerable。 BTW,Person类不需要实现INPC – TypeDescriptor绑定/ Mode = OneTime在这种情况下没有任何区别,我也validation了。 BTW,绑定到IEnumerable / IEnumerable / IList被包装到EnumerableCollectionView内部类中。 不幸的是,我没有机会通过MS.Internal / System.ComponentModel代码来找出ObservableCollection在设置DataContext = null时的工作原理,可能是因为Microsoft人员在取消订阅CollectionChanged时进行了特殊处理。 通过MS.Internal / ComponentModel可以浪费一些宝贵的一生时间:)希望它有所帮助