Имеет ли утечка памяти 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(); 
}

Ответ 1

Я изначально разместил это как комментарий, но я думаю, что он дает лучший ответ, поэтому...

a) если вы уверены, что обнаружили проблему с платформой .NET, вы, вероятно, делаете что-то неправильно. Это не невозможно, это просто маловероятно. b) что GC.Collect() не собирается делать то, что вы думаете.

Мне кажется, вам нужно посмотреть, как работает GC.Collect().


Метод MSDN GC.Collect

Примечания

Используйте этот метод, чтобы попытаться восстановить всю недоступную память.

Все объекты, независимо от того, сколько времени они были в памяти, рассматриваются для сбора; однако объекты, на которые ссылаются управляемый код, не собираются. Используйте этот метод, чтобы заставить систему попытаться восстановить максимальный объем доступной памяти.


Во-первых, вы не показываете нам, где вы распоряжаетесь этой памятью, что ListCollectionView(stuff). Вы просто выделяете новые и выделяете новые, но вы никогда не распоряжаетесь старым. Так что да, он будет течь, как сумасшедший. Пока GC не запускается и не пытается собрать.

Если вы делаете то же самое, что демонстрируете здесь со списком строк, он, скорее всего, сделает то же самое. Но для того, что вы показали, я ожидаю, что он просочится.

Ответ 2

Документация для 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();
    }

Ответ 3

когда вы вызываете GC.Collect, результат переменной по-прежнему находится в области видимости, поэтому он не будет собран, так как есть один указатель на данные. так или иначе, даже если это не так. какая сборка мусора является недетерминированной в отношении кода приложения. как drachenstern сказал, что он только попытается! и это будет в конечном итоге, но вы не можете быть уверены, когда!

Ответ 4

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

Ответ 5

С каждой итерацией result переназначается так, что не будет ссылки на ListCollectionView из предыдущей итерации. Но вызов GC.Collect предусматривает только эти элементы, чтобы вернуть их память, когда CLR решает сделать фактическую сборку мусора. Если вы хотите, чтобы память была исправлена ​​раньше, попробуйте добавить GC.WaitForPendingFinalizers(); сразу после вашего вызова на GC.Collect();.

Ответ 6

Лучший способ сделать это - использовать функции Scopes/Anonymous. 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