Does ListCollectionView leak memory?

女生的网名这么多〃 提交于 2019-12-05 01:11:41

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.

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

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!

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

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

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!