绑定在WPF中会导致内存泄漏吗?

56

当项目消失时,我需要取消绑定来预防内存泄漏吗?我想我只是有点担心,如果我重新加载并且在控件的新模板中应用了一个绑定到外部元素,那么这会阻止为该模板创建的控件被垃圾回收吗?

3个回答

85

如果您没有绑定到DependencyProperty或实现了INotifyPropertyChanged的对象,则绑定可能会泄漏内存,并且在完成后需要解除绑定。

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

因为绑定必须继续监听更改。这种行为使得目标仍在使用时,保持PropertyDescriptorobject之间的引用活动。这可能会导致object和任何object引用的内存泄漏,包括数据绑定目标。

因此,简而言之,如果您绑定到DependencyPropertyINotifyPropertyChanged对象,则应该没问题,否则,就像订阅事件一样,您应该取消订阅绑定。


编辑: 有可能在.NET4.5中使用了Weak Events/References修复了此问题,但在快速测试后,我觉得情况仍然一样,我需要更深入地了解才能确认,所以我个人认为在4.5中可能已经修复了该问题 :)


@sa_ddam213:DP和INotifyPropertyChanged都是绝对安全的。不过,你确定PD没问题吗?我的意思是,在最新的.NET 4.5框架中,弱事件模式被广泛使用,事情应该会变得很好。 - Mario Vernari
我刚刚使用.NET4.5进行了双重检查,当使用PropertyDescriptor时它仍然似乎有泄漏。如果你想确认,我使用了Jossef Goldberg的WPF泄漏测试,并重新编译为4.5。链接:http://blogs.msdn.com/b/jgoldb/archive/2008/02/04/finding-memory-leaks-in-wpf-based-applications.aspx 由于之前版本中绑定的泄漏问题永久性地改变了我的WPF编码习惯,我对4.5中的泄漏没有做太多测试 : ),希望这个问题已经被解决了,我会在上面添加关于4.5的免责声明。 - sa_ddam213
2
那么只读属性的绑定呢?例如,绑定到ExpressionBody成员?public string Test =>“TestString”; - 3615
2
注意:上述提到的内存泄漏问题仅在绑定模式为OneWay或TwoWay时发生。如果绑定是OneTime或OneWayToSource,则CLR不会创建强引用事件,如果绑定属性不是DependencyProperty或绑定对象未实现INotifyPropertyChanged。 - jchristin
1
是否有可能浏览用户控件的可视树中的所有绑定,检测是否存在错误类型并清除它们?或者为什么不在卸载事件中使用BindingOperations.ClearBinding清除所有绑定,无论它们的类型如何。或者因为某些原因这样做很愚蠢? - FKDev
2
它并不是固定的。.NET Framework 4.7.1 有相同的行为。 - Lumo

13

本文仅供参考,不是答案。在经典文章Finding Memory Leaks in WPF-based applications中,作者Jossef Goldberg详细描述了WPF应用程序中可能存在内存泄漏的情况。实际上,大多数情况涉及.NET 3.5/4.0,但有些情况可能至今仍然相关。此外,还有一个小的扩展

引用关于Binding泄漏的内容:

原因:

这个泄漏在这篇kb文章中有记录。它被触发是因为:

TextBlock控件绑定到一个对象(myGrid),该对象又引用回TextBlock(它是myGrid子对象之一)。

注意:这种类型的DataBinding泄漏是特定场景下独特的(而不是所有DataBinding场景),如kb文章所述。在Path中的属性不是DependencyProperty,也不是实现了INotifyPropertyChanged接口的类,并且还必须存在一系列强引用链。

代码:

myDataBinding = new Binding("Children.Count");
myDataBinding.Source = myGrid; 
myDataBinding.Mode = BindingMode.OneWay;
MyTextBlock.SetBinding(TextBlock.TextProperty, myDataBinding);

同样有漏洞的代码也可以用XAML编写:

<TextBlock Name="MyTextBlock" 
           Text="{Binding ElementName=myGrid, Path=Children.Count}" />

修复/解决方法:

有几种方法可用,最简单的方法是在窗口即将关闭时清除绑定。

例如:

BindingOperations.ClearBinding(MyTextBlock, TextBlock.TextProperty);

另一种方法是将数据绑定的模式设置为 OneTime。请参见 kb文章 获取其他想法。

有用的链接:

使用DataBinding避免WPF内存泄漏


更新的链接:https://learn.microsoft.com/en-us/archive/blogs/jgoldb/finding-memory-leaks-in-wpf-based-applications - Alexander Høst

8
http://msdn.microsoft.com/en-us/library/aa970850.aspx得知,WPF使用弱事件模式,不会对对象保持强引用,如果它们是对象的唯一引用,则允许对它们进行GC处理。

"WPF数据绑定的许多方面已经应用了弱事件模式来实现事件。"

但是,弱事件仅用于DependencyPropertiesINotifyPropertyChanged对象,因此如果您绑定到具有除OneTime之外的绑定模式的POCO,则可能会导致内存泄漏。

未来参考,POCO 意味着“普通的 CLR 对象”。 - Lyndon Gingerich

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接