Переменные делегата не собирают мусор - PullRequest
1 голос
/ 21 января 2010

Недавно обнаружил, что переменные внутри ToGadget, а также, вероятно, и делегат, не собирали мусор. Кто-нибудь может понять, почему .NET содержит ссылку на это? Кажется, что делегат и все будут помечены для сборки мусора после завершения Foo. Буквально увидел B иллюзий в памяти после сброса кучи.

Примечание: «result.Things» - это список (), а Converter - системный делегат.


        public Blah Foo()
        {
                var result = new Blah();
                result.Things = this.Things.ConvertAll((new Converter(ToGadget)));
                return result;
        }
        .................
        public static Gadget ToGadget(Widget w)
        {
            return new Gadget(w);
        }

Обновление : изменение ConvertAll на это очищает делегатов и соответствующие ссылки на объекты. Это говорит мне о том, что либо List <> ConvertAll каким-то образом удерживает делегата, либо я не понимаю, как эти вещи собираются мусором.


            foreach (var t in this.Things)
            {
                result.Things.Add(ToGadget(t));         
            }

Ответы [ 3 ]

6 голосов
/ 21 января 2010

Используйте профилировщик памяти.

Вы можете задать вопрос в StackOverflow весь день и получить кучу образованных догадок, или вы можете добавить профилировщик памяти в свое приложение и сразу увидеть, что укоренилось и что является мусором. Доступны инструменты, специально разработанные для быстрого и простого решения вашей конкретной проблемы. Используйте их!

4 голосов
/ 21 января 2010

В вашем вопросе есть один серьезный недостаток, который может быть причиной путаницы:

Кажется, что делегат и все будут помечены для сборки мусора после завершения Foo.

CLR не "помечает элементы" для сбора в конце процедуры. Скорее, когда эта процедура заканчивается, больше нет (активной) ссылки ни на один из элементов, на которые ссылается ваш делегат. В этот момент они называются «некорневыми».

Позже , когда CLR определяет, что существует определенная нагрузка на память, сборщик мусора будет выполняться. Он будет искать и находить все некорневые элементы и потенциально собирать их.

Важным отличием здесь является то, что время не может быть предсказано. Объекты могут никогда не собираться до тех пор, пока ваша программа не закончится, или они могут быть собраны сразу же. Система должна определить, когда она будет собираться. Этого не происходит, когда заканчивается Foo - скорее, через какое-то неизвестное время после окончания Foo.


Edit:

Это на самом деле напрямую касается вашего вопроса, кстати. Вы можете увидеть, если это проблема, форсировать сборку мусора. Просто добавьте после вашего звонка в Foo звонок на:

GC.Collect();
GC.WaitForPendingFinalizers();

Затем выполните проверку кучи CLR. На этом этапе, если вы все еще получаете объекты в куче, это потому, что объекты все еще чем-то укоренены. Ваш упрощенный пример не показывает, что это происходит, но так как это очень упрощенный пример, трудно определить, где это произойдет. (Примечание: я не рекомендую сохранять это в своем коде, если это так. Вызов GC.Collect () вручную почти всегда является плохой идеей ...)

2 голосов
/ 21 января 2010

Похоже, ваша функция настроена на возврат нового Blah (). Это действительно возвращается в вашем коде? Я вижу в опубликованной вами статье, что это не так. Если это так, то новый Blah () будет иметь область за пределами Foo, и это может быть вызывающая функция, которая фактически удерживает ссылки в области. Кроме того, вы также создаете новый гаджет (). В зависимости от того, сколько у вас Blahs для гаджетов, вы можете экспоненциально заполнить вашу память, поскольку гаджеты будут ограничены Blahs, которые затем будут удерживаться в области действия за пределами Foo.

Прав ли я или нет, эту возможность было довольно забавно набирать.

...