Утечка памяти ListCollectionView? - PullRequest
       33

Утечка памяти ListCollectionView?

10 голосов
/ 22 апреля 2011

Я исследовал, как избежать утечек памяти, вызванных сильными ссылками на событие 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 ]

10 голосов
/ 22 апреля 2011

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

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

Я думаю, вам нужно проверить, как работает GC.Collect ().


MSDN GC. Метод сбора

Примечания

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

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


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

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

8 голосов
/ 24 ноября 2015

Документация для 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 голосов
/ 22 апреля 2011

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

1 голос
/ 27 ноября 2015

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

0 голосов
/ 01 декабря 2015

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

0 голосов
/ 30 ноября 2015

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

...