Monday, August 23, 2010

Removing Event Handlers using Reflection

Introduction
I've recently been working on a pretty massive WinForms project which is very complex. We noticed that the application was 'leaking' memory (i.e. the memory usage was continually growing despite objects being correctly 'Disposed').
Having run the application through SciTech's excellent .NET Memory Profiler we discovered the cause : "Direct EventHandler Roots".
One of the biggest causes of "memory leaks" in a .NET application is when an unintended reference keeps a Managed object alive (the other being Managed objects which hold Unmanaged resources).
Simple example :-  
A.SomeEvent += new EventHandler(B.SomeMethod);
Later, you finish with B (but not with A) and Dispose of it. However because A is holding a reference to B (via an EventHandler), B will never get garbage collected while A is still alive.
Big deal? Can't you just do :-
A.SomeEvent -= new EventHandler(B.SomeMethod);
Absolutely! And that will de-couple the objects and allow them to be GC'd correctly.
HOWEVER, in the real world (and especially in big complex applications which have grown 'organically' over time) you can find yourself dealing with dozens of different object types and scores of object instances where the interplay between the classes via Delegates and EventHandlers can be nightmarish to work through.
Furthermore, when EventHandlers / Delegates are assigned dynmically at runtime, it may be impossible to know at any one time what Delegates are assigned to what EventHandlers, thus making it impossible to use -= to remove the appropriate delegate. And what about removing anonymous delegates? It all gets very hairy.
Of course, if you know from the outset that you're going to use this sort of "EventHandler-heavy" model, then you should really use Weak Referenced EventHandlers. (Please see John Stewien's excellent article about this for more information.)
Anyway, if you find yourself in a fix with an application which is leaking memory due to rooted EventHandler references, and you don't have time to refactor the code or add -= counterparts to all the +='s, then this may help!
Read more: Codeproject