I am currently debugging a large (very large!) C # application that contains memory leaks. It mainly uses Winforms for the GUI, although several controls are created in WPF and hosted with ElementHost. So far, I have discovered that many memory leaks were caused by events that were not detached (by calling - =), and I was able to solve the problem.
However, I ran into a similar problem. There is a class called WorkItem (short-lived), which is registered in the constructor for events of another class ClientEntityCache (long-lived). Events were never untied, and I could see in the .NET profiler that WorkItem instances are kept alive when they shouldn't because of these events. Therefore, I decided to force WorkItem to implement IDisposable, and in the Dispose () function, I canceled the events this way:
public void Dispose() { ClientEntityCache.EntityCacheCleared -= ClientEntityCache_CacheCleared;
EDIT
Here is the code I use to subscribe:
public WorkItem() { ClientEntityCache.EntityCacheCleared += ClientEntityCache_CacheCleared;
I also changed the code for unregistering so as not to call the new EntityCacheClearedEventHandler.
END OF EDITING
I made Dispose calls in the appropriate places in the code that the WorkItem uses, and when I debug it, I see that the function is actually being called, and I am = for each event. But I still have a memory leak, and my WorkItems are still alive after Disposed and in the .NET profiler I see that the instances remain alive because event handlers (like EntityCacheClearedEventHandler) still have them in your call list. I tried to unhook them more than once (several - =) to make sure that they were not connected more than once, but this does not help.
Does anyone have an idea why this is happening or what can I do to solve the problem? I believe that I can change event handlers to use weak delegates, but this will require a lot of work with a lot of legacy code.
Thanks!
EDIT:
If this helps, here is the root path described by the .NET profiler: many things point to ClientEntityCache, which points to EntityCacheClearedEventHandler, which points to Object [], which points to another instance of EntityCacheClearedEventHandler (I donβt understand why), which points to WorkItem.