В настоящее время я отлаживаю большое (очень большое!) С#-приложение, которое содержит утечки памяти. Он в основном использует Winforms для графического интерфейса, хотя несколько элементов управления созданы в WPF и размещены на ElementHost. До сих пор я обнаружил, что многие утечки памяти были вызваны тем, что события не были отцеплены (путем вызова -=), и мне удалось решить эту проблему.
Однако я только что столкнулся с подобной проблемой. Существует класс WorkItem (короткоживущий), который в конструкторе регистрируется на события другого класса с именем ClientEntityCache (долгоживущий). События никогда не отключались, и я мог видеть в профилировщике .NET, что экземпляры WorkItem сохранялись, когда они не должны были из-за этих событий. Поэтому я решил реализовать IDisposable в WorkItem, а в функции Dispose() я отвязываю события следующим образом:
public void Dispose()
{
ClientEntityCache.EntityCacheCleared -= ClientEntityCache_CacheCleared;
// Same thing for 10 other events
}
РЕДАКТИРОВАТЬ
Вот код, который я использую для подписки:
public WorkItem()
{
ClientEntityCache.EntityCacheCleared += ClientEntityCache_CacheCleared;
// Same thing for 10 other events
}
Я также изменил код для отмены регистрации, чтобы не вызывать новый EntityCacheClearedEventHandler.
КОНЕЦ РЕДАКТИРОВАНИЯ
Я сделал вызовы Dispose в нужных местах кода, который использует WorkItem, и когда я отлаживаю, я вижу, что функция действительно вызывается, и я делаю -= для каждого события. Но я все еще получаю утечку памяти, и мои рабочие элементы все еще остаются в живых после удаления, и в профилировщике .NET я вижу, что экземпляры остаются в живых, потому что обработчики событий (такие как EntityCacheClearedEventHandler) все еще имеют их в своем списке вызовов. Я пытался отцепить их более одного раза (несколько -=), просто чтобы убедиться, что они не были подключены более одного раза, но это не помогает.
Кто-нибудь знает, почему это происходит или что я могу сделать, чтобы решить проблему? Я полагаю, что мог бы изменить обработчики событий, чтобы использовать слабые делегаты, но это потребовало бы много возни с большой кучей устаревшего кода.
Спасибо!
РЕДАКТИРОВАТЬ:
Если это поможет, вот корневой путь, описанный профилировщиком .NET: многие вещи указывают на ClientEntityCache, который указывает на EntityCacheClearedEventHandler, который указывает на Object[], который указывает на другой экземпляр EntityCacheClearedEventHandler (я не понимаю, почему), который указывает на WorkItem.