Расширенный совет по отладке в WPF GarbageCollection

Ситуация

Мы запускаем большое приложение WPF, которое не выпускает память в течение некоторого времени. Это не настоящая утечка памяти, так как память скоро будет выпущена. Я знаю, что это нормально, это не будет рассматриваться как проблема. К сожалению, это становится проблемой производительности вместе с инфраструктурой управления WPF. Подробнее см. Ниже.

Выводы

У нас есть автоматические тесты, которые выполняют типичные варианты использования. Некоторые случаи работают нормально и освобождают память во времени. Другие блокируют память до тех пор, пока клиент не будет сведен к минимуму, не откроется новое окно или не появятся другие условия, которые запускают коллекцию Gen2.

• С ANTS мы видим, что объекты не имеют GC Root, но много ссылок на другие объекты, которые требуют завершения.

• WinDbg не показывает, какие объекты будут готовы для завершения.

• Запуск нескольких GC.Collect(), GC.WaitForPendingFinalizers() полностью освобождает память.

• Мы знаем, какое действие UI вызывает высокое состояние памяти, но мы не смогли идентифицировать какой-либо подозрительный код.

Вопрос

Мы будем признательны за любые советы по отладке такой проблемы.


Фон > CommandManager WPF

Командный менеджер WPF содержит частную коллекцию WeakReferences (_requerySuggestedHandlers) для создания события CanExecuteChanged. Обработка CanExecuteChanged довольно дорогостоящая (особенно нахождение EventRoute для CanExecute, которое, по-видимому, является RoutedEvent). В любое время, когда CommandManager чувствует себя готовым к выполнению команд, он выполняет итерацию через эту коллекцию и вызывает событие CanExecuteChanged в соответствующих источниках команд.

The WeakReferences не удаляются из этой коллекции, пока есть дескриптор GC для указанного объекта. Хотя объект не был собран, CommandHelper продолжает обрабатывать события CanExecute для этих элементов (ButtonBase или MenuItems). В случае, если много мусора (как в нашем случае), это может привести к чрезвычайно большому количеству вызовов обработчиков событий CanExecute, что приводит к тому, что приложение действительно отстает.

Ответ 1

У меня такая же проблема с одним из моих приложений. При каждом открытии окна я вызываю:

GC.GetTotalMemory(true);

Это заставит GC немедленно очистить память, не дожидаясь ожидания. Подробнее об этом методе можно прочитать здесь:

http://msdn.microsoft.com/en-us/library/system.gc.gettotalmemory.aspx

О проблеме с вызовами CanExecute я стараюсь избегать их из-за тех же проблем с производительностью. Вместо этого я использую свойства в моей модели представления и привязываю свойство IsEnabled визуальных элементов из XAML к свойствам из модели представления. Таким образом, общая производительность улучшена, а вызовы CanExecute не пройдены.

Надеюсь, это поможет.

Ответ 2

Попробуйте CLRProfiler, здесь ссылка для скачивания. Он показывает обработчики событий, распределенные и выжившие. Я уверен, что вы можете проследить основную причину с помощью этого инструмента. В книге Advanced.NET Debugging перечислены некоторые полезные инструменты для отладки, вы можете прочитать ее для некоторой помощи.