ListCollectionView泄漏内存吗?

时间:2011-04-21 20:23:48

标签: c# wpf mvvm memory-leaks listcollectionview

我一直在调查如何避免因视图模型中对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(); 
}

6 个答案:

答案 0 :(得分:10)

我最初将此作为评论发布,但我认为这是一个更好的答案,所以......

a)如果您确定自己发现了.NET框架的问题,那么您可能做错了什么。这不是不可能的,只是不太可能。 b)GC.Collect()不会按照你的想法去做。

我认为您需要查看GC.Collect()的工作原理。


MSDN GC.Collect Method

说明

使用此方法尝试回收所有无法访问的内存。

所有物品,无论它们在记忆中存在多长时间,都被考虑收集;但是,不会收集托管代码中引用的对象。使用此方法强制系统尝试回收最大可用内存量。


对于初学者,你没有告诉我们你在哪里处理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