我一直在调查如何避免因视图模型中对INotifyCollectionChanged
事件的强引用而导致的内存泄漏。我正在玩ListCollectionView
,看看是否能解决这个问题。我认为以下是泄漏记忆,我做错了什么?
var stuff = new ObservableCollection<string>();
while (true)
{
var result = new ListCollectionView(stuff);
// Just to keep make sure that the memory I'm seeing
// isn't waiting to be GC'd
GC.Collect();
}
答案 0 :(得分:10)
我最初将此作为评论发布,但我认为这是一个更好的答案,所以......
a)如果您确定自己发现了.NET框架的问题,那么您可能做错了什么。这不是不可能的,只是不太可能。 b)GC.Collect()不会按照你的想法去做。
我认为您需要查看GC.Collect()的工作原理。
使用此方法尝试回收所有无法访问的内存。
所有物品,无论它们在记忆中存在多长时间,都被考虑收集;但是,不会收集托管代码中引用的对象。使用此方法强制系统尝试回收最大可用内存量。
对于初学者,你没有告诉我们你在哪里处理ListCollectionView(stuff)
的记忆。你只是分配新的和分配新的,但你永远不会丢弃旧的。所以,是的,它会像疯了一样泄漏。在GC运行并尝试收集之前。
如果你做同样的事情,你在这里展示一个字符串列表,它很可能会做同样的事情。但是对于你所展示的内容,我预计它会泄漏。
答案 1 :(得分:8)
ListCollectionView
的文档不是很好但是如果您注意到有方法DetachFromSourceCollection
。这个电话的评论提到取消订阅并允许垃圾收集。
var stuff = new ObservableCollection<string>();
while (true)
{
ListCollectionView result = new ListCollectionView(stuff);
//Use this method to unsubscribe to events on the underlying collection and allow the CollectionView to be garbage collected.
result.DetachFromSourceCollection();
//When finished set to null
result = null;
GC.Collect();
}
答案 2 :(得分:2)
当你调用GC.Collect时,你的变量结果仍然在范围内,所以它不会被收集,因为有一个指向数据的指针。无论如何,即使不是这样。就应用程序代码而言,垃圾收集的作用是非确定性的。像drachenstern说它只会尝试!它最终会成功,但你不能确定什么时候!
答案 3 :(得分:1)
CollectionView包含对源集合的CollectionChanged事件的引用 - 因此在源集合被处理掉并收集之前,GC无法收集视图。
从CollectionView
的文档中也可以清楚地看到这一点 /// <summary>
/// Detach from the source collection. (I.e. stop listening to the collection's
/// events, or anything else that makes the CollectionView ineligible for
/// garbage collection.)
/// </summary>
public virtual void DetachFromSourceCollection()
此博客描述了您的问题并提出了两种可能的解决方案:
http://www.eidias.com/blog/2014/2/24/wpf-collectionview-can-leak-memory ......
答案 4 :(得分:0)
每次迭代result
都被重新分配,因此不会引用前一次迭代中的ListCollectionView
。但是,当CLR决定进行实际的垃圾收集时,调用GC.Collect
仅调度那些项目以回收它们的内存。如果您希望更快地回收内存,请在致电GC.WaitForPendingFinalizers();
后立即添加GC.Collect();
。
答案 5 :(得分:0)
执行此操作的最佳方法是使用范围/匿名函数。兰巴达很感激
var stuff = new ObservableCollection<string>();
ClosureDelegate closure = (x) => {
ListCollectionView result = new ListCollectionView(x);
//Use this method to unsubscribe to events on the underlying collection and allow the CollectionView to be garbage collected.
result.DetachFromSourceCollection();
};
while (true)
{
closure(stuff);
GC.Collect();
}
使用此方法,您的抛出结果将超出范围,因为其方法已被删除,并且这将使用尽可能少的内存。
取自:https://msdn.microsoft.com/en-gb/library/bb882516.aspx?f=255&MSPPError=-2147217396