ListCollectionView 是否泄漏内存?

发布于 2024-11-02 22:13:59 字数 399 浏览 4 评论 0原文

我一直在研究如何避免由于视图模型中对 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(); 
}

I've been investigating how to avoid memory leaks caused by strong references to the INotifyCollectionChanged event from a view model. I was playing around with using a ListCollectionView to see if that would deal with it for me. I think that the following is leaking memory, am I doing something wrong?

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(); 
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

瑾夏年华 2024-11-09 22:13:59

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();
    }

The documentation for ListCollectionView is not great but if you noticed there is a method DetachFromSourceCollection. The remarks for this call mention unsubscribing and allowing garbage collection.

    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();
    }
万劫不复 2024-11-09 22:13:59

我最初将其作为评论发布,但我认为它提供了更好的答案,所以...

a) 如果您确定发现了 .NET 框架的问题,那么您可能做错了什么。这并非不可能,只是不太可能。
b) GC.Collect() 不会做你想象的事情。

我认为您需要回顾一下 GC.Collect() 的工作原理。


MSDN GC.Collect 方法

备注

使用此方法尝试回收所有内存那是无法到达的。

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


首先,您不会向我们展示您在何处处理 ListCollectionView(stuff) 的内存。你只是分配新的并分配新的,但你永远不会处理掉旧的。所以是的,它会疯狂地泄漏。直到 GC 运行并尝试收集。

如果您使用字符串列表执行此处演示的相同操作,它很可能会执行相同的操作。但就你所展示的内容而言,我预计它会泄露。

I initially posted this as a comment, but I think it makes a better answer, so ...

a) if you're sure you've found a problem with the .NET framework, you're probably doing something wrong. It's not impossible, it's just not likely.
b) that GC.Collect() isn't going to do what you're thinking it will.

I think you need to review how GC.Collect() works.


MSDN GC.Collect Method

Remarks

Use this method to try to reclaim all memory that is inaccessible.

All objects, regardless of how long they have been in memory, are considered for collection; however, objects that are referenced in managed code are not collected. Use this method to force the system to try to reclaim the maximum amount of available memory.


For starters, you don't show us where you're disposing of that memory that the ListCollectionView(stuff). You're just allocating new and allocating new, but you never dispose of the old. So yeah, it's going to leak like crazy. Until the GC runs and tries to collect.

If you do the same thing you demonstrate here with a list of strings it will most likely do the same thing. But for what you've shown, I expect it to leak.

梦屿孤独相伴 2024-11-09 22:13:59

当您调用 GC.Collect 时,变量结果仍在范围内,因此不会收集它,因为有一个指向数据的指针。无论如何,即使事实并非如此。就应用程序代码而言,垃圾收集的作用是不确定的。就像drachenstern说的,它只会尝试!它最终会成功,但你不能确定什么时候!

when you call GC.Collect, you variable result is still in scope so it won't be collected since there is one pointer to the data. anyway even if it wasn't the case. what garbage collection does is non deterministic as far as application code is concerned. like drachenstern said it will only try! and it will succeed eventually but you can't be sure when!

吻泪 2024-11-09 22:13:59

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-内存 ...

The CollectionView holds a reference to the source collection’s CollectionChanged event - hence GC can not collect the view until the source collection is disposed off and collected.

This is also clear from the documentation of 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()

This blog describes your issue and suggest two possible solutions:
http://www.eidias.com/blog/2014/2/24/wpf-collectionview-can-leak-memory ...

甩你一脸翔 2024-11-09 22:13:59

每次迭代都会重新分配结果,以便不会引用上一次迭代中的ListCollectionView。但是,调用 GC.Collect 只会在 CLR 决定执行实际垃圾回收时安排这些项目回收其内存。如果您希望更快地回收内存,请尝试在调用 GC.Collect(); 之后立即添加 GC.WaitForPendingFinalizers();

With each iteration result is being reassigned so that there won't be a reference to the ListCollectionView from the previous iteration. But calling GC.Collect only schedules those items to have their memory reclaimed when the CLR decides to do the actual garbage collection. If you would like to see the memory getting reclaimed sooner, try adding GC.WaitForPendingFinalizers(); immediately after your call to GC.Collect();.

漆黑的白昼 2024-11-09 22:13:59

最好的方法是使用作用域/匿名函数。 Lambada 很高兴

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

The best way to do this is to use Scopes / Anonymous functions. Lambada is grate for that

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(); 
}

Using this method your throwing result out of scope as its method has been removed and this would use as little memory as possible.

Taken from: https://msdn.microsoft.com/en-gb/library/bb882516.aspx?f=255&MSPPError=-2147217396

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文