绑定会在 WPF 中造成内存泄漏吗?

Posted

技术标签:

【中文标题】绑定会在 WPF 中造成内存泄漏吗?【英文标题】:Can bindings create memory leaks in WPF? 【发布时间】:2013-09-03 18:36:42 【问题描述】:

我是否需要在项目消失时取消绑定项目以防止内存泄漏?我想我只是有点担心,如果我重新加载并将新模板应用于控件,并且在该模板中存在与外部元素的绑定,是否会阻止为模板制作的控件被垃圾收集?

【问题讨论】:

【参考方案1】:

如果您没有绑定到 DependencyProperty 或实现 INotifyPropertyChanged 的对象,则绑定可能会泄漏内存,完成后您必须解除绑定。

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

因为绑定必须继续监听变化。此行为使 PropertyDescriptorobject 之间的引用保持活动状态,因为目标仍在使用中。这可能会导致objectobject 所引用的任何object 中的内存泄漏,这包括数据绑定目标。

简而言之,如果您绑定到 DependencyPropertyINotifyPropertyChanged 对象,那么您应该没问题,否则就像任何订阅的事件一样,您应该取消订阅您的绑定


编辑: 有可能这是在 .NET4.5 中使用弱事件/引用修复的,但经过几次快速测试后,我觉得这似乎是一样的,我将不得不更深入地进行确认,所以我个人会在 可能在 4.5 中修复 :)

【讨论】:

@sa_ddam213:DP 和 INotifyPropertyChanged 都是安全的。无论如何,你确定PD?我的意思是,在最新的 .Net 4.5 框架上,事情应该会变得很好,其中 WeakEvent 模式被大量使用。 如何绑定到只读属性?例如,绑定到 ExpressionBody 成员?公共字符串 Test => "TestString"; 注意:上面提到的内存泄漏问题只有在绑定模式为 OneWay 或 TwoWay 时才会出现。如果绑定是 OneTime 或 OneWayToSource,如果绑定属性不是 DependencyProperty 或绑定对象未实现 INotifyPropertyChanged,则 CLR 不会创建强引用事件。 是否可以浏览用户控件的可视化树中的所有绑定,检测是否存在错误类型并清除它们?或者为什么不使用卸载事件中的 BindingOperations.ClearBinding 清除所有类型的绑定。还是出于某种原因愚蠢? 它不是固定的。 .NET Framework 4.7.1 具有相同的行为。【参考方案2】:

不假装回答,仅供参考。在Finding Memory Leaks in WPF-based applications作者Jossef Goldberg上的一篇经典文章中,详细描述了WPF应用程序中可能存在内存泄漏的情况。确实,大多数与 .NET 3.5/4.0 有关,但有些情况可能与今天有关。另外,有一个小的extension。

引用Binding中的泄漏:

Cause:

此泄漏记录在此 kb article 中。它被触发是因为:

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

Note: 这种类型的 DataBinding 泄漏对于特定场景(而不是所有 DataBinding 场景)是唯一的,如 kb article 中所述。 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" />

Fix/Workaround:

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

例如:

BindingOperations.ClearBinding(MyTextBlock, TextBlock.TextProperty);

其他方法是将数据绑定的模式设置为 OneTime。其他想法请参见kb article。

有用的链接:

Avoiding a WPF memory leak with DataBinding

【讨论】:

更新链接:docs.microsoft.com/en-us/archive/blogs/jgoldb/…【参考方案3】:

从http://msdn.microsoft.com/en-us/library/aa970850.aspx 开始,WPF 使用弱事件模式,它不持有对对象的强引用,如果它们是对对象的唯一引用,则允许它们被 GC。

“WPF数据绑定的很多方面已经有了弱事件模式 应用于事件的实现方式。”

但弱事件仅用于DependencyPropertiesINotifyPropertyChanged 对象,因此如果您使用OneTime 以外的绑定模式绑定到POCO,您最终可能会泄漏内存。

【讨论】:

以上是关于绑定会在 WPF 中造成内存泄漏吗?的主要内容,如果未能解决你的问题,请参考以下文章

闭包会造成内存泄漏吗?

闭包会造成内存泄漏吗

WPF DataGrid ItemsSource 内存泄漏

防止 WPF 中的内存泄漏

闭包会造成内存泄漏吗?

闭包会造成内存泄漏吗?