当我将ListBox的ItemsSource绑定到List时,绑定引擎在控件消失后保持列表元素。这会导致所有列表元素保留在内存中。使用ObservalbleCollection时问题就消失了。为什么会这样?
窗口标记内的xaml
<Grid>
<StackPanel>
<ContentControl Name="ContentControl">
<ListBox ItemsSource="{Binding List, Mode=TwoWay}" DisplayMemberPath="Name"/>
</ContentControl>
<Button Click="Button_Click">GC</Button>
</StackPanel>
</Grid>
代码背后:
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<Person> _list =
new List<Person> { new Person { Name = "one" }, new Person { Name = "two" } };
public IEnumerable<Person> List
{
get { return _list; }
set
{
_list = value;
RaisePropertyChanged("List");
}
}
class Person
{
public string Name { get; set; }
}
编辑:为了检查人员泄漏,我使用了ANTS和.Net内存分析器。两者都显示在按下GC按钮后,只有绑定引擎保持对人物对象的引用。
答案 0 :(得分:6)
您将内容设置为null,因此您终止了强制ListBox但仍然将ItemsSource绑定到List,因此ListBox内存未完全释放。
遗憾的是,这是一个众所周知的问题,也在MSDN上有详细记载。
如果您没有绑定到DependencyProperty或实现INotifyPropertyChanged或ObservableCollection的对象,那么绑定可能会泄漏内存,并且您必须在完成后取消绑定。
这是因为如果对象不是DependencyProperty或者没有实现INotifyPropertyChanged或者没有实现INotifyCollectionChanged(正常列表没有实现这个),那么它通过PropertyDescriptors AddValueChanged方法使用ValueChanged事件。这会导致CLR从PropertyDescriptor创建一个强引用到对象,在大多数情况下,CLR将在全局表中保留对PropertyDescriptor的引用。
因为绑定必须继续侦听更改。当目标保持使用时,此行为使PropertyDescriptor和对象之间的引用保持活动状态。这可能导致对象中的内存泄漏以及对象引用的任何对象。
问题是......是实施INotifyPropertyChanged的人吗?
答案 1 :(得分:1)
我看了一下你的JustTrace内存分析器的例子,除了一个明显的问题,为什么你会杀死视图模型/ nullify DataContext并让视图保持运行(在99.9%的情况下你会杀掉View和DataContext - 因此ViewModel和Bindings自动变换范围这里是我找到的。
如果您将示例修改为:
,它将正常工作以上修改证明您可以安全地在绑定中使用IEnumerable / IEnumerable。 BTW,Person类不需要实现INPC - TypeDescriptor绑定/ Mode = OneTime在这种情况下没有任何区别,我也验证了。 BTW,绑定到IEnumerable / IEnumerable / IList被包装到EnumerableCollectionView内部类中。不幸的是,我没有机会通过MS.Internal / System.ComponentModel代码来找出ObservableCollection在设置DataContext = null时的工作原理,可能是因为微软的人在取消订阅CollectionChanged时做了特别处理。通过MS.Internal / ComponentModel可以浪费一些宝贵的一生时间:)希望它有所帮助