При каких обстоятельствах нам нужно дважды вызвать GC.Collect

У нас есть приложение WPF, основанное на Unity с шаблоном MMVVVM. В жизненном цикле применения может быть несколько жизненных циклов проекта, после каждого жизненного цикла проекта мы выполняем ручную Tear Down и пытаемся освободить все ссылки ViewModels. Для подписки на события с Unity мы используем Слабые ссылки. Поэтому мы предполагаем, что после разрыва мы можем вызвать GC Collect, чтобы все мусорные объекты собирали мусор. У нас есть еще одна возможность вручную отказаться от подписки на все события, но мы предпочитаем сборку мусора, потому что она очистит около 200 МБ для нас, что облегчит загрузку нового проекта.

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

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

Но если я попробую вызвать GC дважды подряд, он полностью очистит все.

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

Любые мысли или указатели будут высоко оценены.

Обновление:

В классе нет финализаторов.

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

Ответ 1

Похоже, что у вас есть что-то с финализатором, в основном - если вы только вызываете GC.Collect() один раз, финализаторы заканчивают, но финализированные объекты не собираются.

Независимо от того, является ли это ошибкой, другое дело. Как правило, это не очень хорошая идея, чтобы там были финализаторы, которые на самом деле должны выполняться, но, возможно, в вашем случае это нормально.

Ответ 2

Если я вызову GC.Collect только один раз, его ссылка все еще остается в памяти на некоторое время.

Не очень странно. Когда объект имеет Finalizer (и на нем не было вызвано GC.SuppressFinalize()), он получает приостановление выполнения (он не собирается, так что финализатор может работать с действительными объектами). Все экземпляры, на которые ссылается этот объект, также получают приостановление исполнения. Второй раунд через GC необходим для очистки всего.

С другой стороны, большинство программ, включая большие и сложные, должны иметь возможность запускать без вызова GC.Collect() даже один раз. И вы хотите назвать это дважды...

после каждого жизненного цикла проекта мы делаем ручную срыв

Звучит сложно и легко избежать... Сколько ссылок есть в ваших моделях Domain/View? В идеале вы просто отрезали бы 1 или 2 ссылки на "главный" объект и забыли об этом.

Ответ 3

@Nitin, я не уверен, поможет ли мое предложение или нет, но, как правило, следует избегать явного вызова GC.Collect(). потому что это может вызвать проблемы с производительностью. Вместо этого попробуйте следовать правильному шаблону удаления.

Ответ 4

У вас есть слабая ссылка, которую вы ожидаете установить равной нулю, поскольку вы вызываете GC, и вы считаете, что ничего не удерживает объект.

Я не знаю, как слабые ссылки реализованы в .net, но очень возможно, что они задерживают сбор объектов и используют такую ​​систему, как finalizerrs.

Или это может быть у вас есть объект, который необходимо завершить, поскольку вы не вызываете dispose() на нем. (Очень часто с WPF или WinForms)

Чтобы узнать, вам нужно использовать MemoryProfiler, рассчитывать потратить хотя бы на день, опираясь на использование любого профилировщика памяти, так как вам будет представлено множество данных об объектах, внутренних для WPF. Лично я бы скачать бесплатный трейл Redgates memory profiler и поймать, что проблема, там поддержка также полезна. (Другие профилировщики памяти также работают хорошо, в значительной степени это зависит от того, к чему вы привыкли.)