This blog post describes a simple technique for ensuring that consumers of events unsubscribe their event handlers without the need for weak events.
I think the concept of managed memory, where the cleanup of unused objects from the heap is performed by a garbage collector, is a fantastic idea. It means that developers working with Java or C# (or other CLR languages) can often forget all about memory allocation, concentrating on more interesting tasks. However, whereas in the Java language the concept of memory leaks has almost completely vanished, they unfortunately rear their ugly head all to often when developing applications for the Microsoft Common Language Runtime (CLR). This is almost entirely down to one thing, events.
The problem with events is that they form a strong reference (i.e. a link between two object instances that prohibits garbage collection) in a manner that is not immediately obvious due to the syntax that event subscription uses. When subscribing to an event, the following syntax is used:
// subscribe to an event textBox.TextChanged += new EventHandler(TextBox_TextChanged); // or textBox.TextChanged += TextBox_TextChanged; // remove the subscription textBox.TextChanged -= new EventHandler(TextBox_TextChanged); // or textBox.TextChanged -= TextBox_TextChanged;
Note the second examples use the shortened syntax where the delegate instance, EventHandler, is not explicitly constructed but added by the compiler, there is no difference semantically.
By subscribing to an event the following relationships are constructed, with the direction of reference as indicated:
As you can see from the above, adding an handler to an event creates a strong reference from source to listener. This does not cause significant problems if the listener has a shorter or the same lifecycle that the source, as is the case where you add event handlers for controls in a Windows Form for example. However, if the source has a longer lifecycle than the listener, and the listener fails to remove its event subscription, a memory leak will occur.
In practice this kind of problem often occurs in modular applications where there exists some sort of Shell or Environment that raises events or acts as a mediator. Within this hosting environment their exists numerous loosely coupled modules which by necessity have a shorter lifecycle than their container. If a module subscribes to events from the Shell but fails to unsubscribe at the end of its life a memory leak occurs. In complex systems this happens surprisingly often!
A common solution to this problem is to use Weak Events. These are events where either the reference between source and listener is weak, meaning that listener can be garbage collected if it is only referenced via this event handler. An early implementation of this pattern was developed for WPF and is provided by Greg Schechter on his blog. There is also an excellent codeproject article by Daniel Grunwald which details many different approaches to creating weak event listeners and sources.
However, whilst liberally sprinkling weak events about your code will solve potential memory leaks, it should not be used as a replacement for proper event subscription clean-up. What if a module’s event handler performs non-trivial logic? Do you really want this to occur after the module is supposed to have been destroyed? So, how do you ensure that events are being unsubscribed? This can be done by code-review, or by making use of heap profiling tools such as dotTrace. With these tools you can run your application, create and destroy modules, garbage collect then inspect your heap to see that the modules and their related objects have been removed. If not, you can trace the object references to find the offending event handler. However, this is a time consuming process.
An alternative is to make use of the fact that events are simply delegates with a highly restrictive interface applied. However, within the class where the event is defined you can treat it as an delegate, allowing you to inspect its invocation list to find all the event handlers. Therefore, if you have a long-lived service component that raises events which are handled by shorter lived modules, you can check the event invocation list after all the modules have been destroyed at application shutdown to ensure that all modules have correctly unsubscribed. This can be achieved as follows:
/// <summary> /// An event which indicates that some stock price has changed /// </summary> public event EventHandler<StockPriceUpdateEventArgs> StockPriceUpdate; public void Dispose(object sender, EventArgs e) { // check that when this service is destroyed, that there are no event // subscriptions CheckEventHasNoSubscribers(StockPriceUpdate); } /// <summary> /// Detects whether an event has any subscribers /// </summary> [Conditional("DEBUG")] private void CheckEventHasNoSubscribers(Delegate eventDelegate) { if (eventDelegate != null) { // if the event has any subscribers, create an informative error message. if (eventDelegate.GetInvocationList().Length != 0) { int subscriberCount = eventDelegate.GetInvocationList().Length; // determine the consumers of this event StringBuilder subscribers = new StringBuilder(); foreach (Delegate del in eventDelegate.GetInvocationList()) { subscribers.Append((subscribers.Length != 0 ? ", " : "") + del.Target.ToString()); } // name and shame them! Debug.WriteLine(string.Format("Event: {0} still has {1} subscribers, with the following targets [{2}]", eventDelegate.Method.Name, subscriberCount, subscribers.ToString())); } } }
Here we have an event which our service raises, on disposal we check that the event has no subscribers. If any modules have failed to cleanup properly we see the following message:
Event: PriceService_StockPriceUpdate still has 2 subscribers, with the following targets: [MemoryLeakExamples.StockPriceViewer, Text: StockPriceViewer, MemoryLeakExamples.StockPriceViewer, Text: StockPriceViewer]
This message tells us how many classes still have referenced event handlers, and their type. This should make it very easy to pinpoint the memory leak.
The sourcecode for this blog post includes a very simple WinForms application where a Stock Price service raises events that are handled by simple UI modules that can be created and destroyed by the user. Run it in DEBUG mode to see the memory leaks! Look in StockPriceViewer to see how to fix the problem.
Download the sourcecode: MemoryLeakExample.zip
Regards, Colin E.
Tags: memory leaks


email: ceberhardt@scottlogic.co.uk




“This does not cause significant problems if the listener has a shorter or the same lifecycle that the source, as is the case where you add event handlers for controls in a Windows Form for example.”
This sentence is backwards. There are not problems if the *source* has a shorter or equal lifetime as the *listener*, because it is the source which is preventing the listener from being collected.
Some of your links don’t seem to work.
@Ashley,
Which one are not working for you? If you let me know I can fix them
Colin E.
Let me clarify this, “It’s easy to make a strong ref to an object from a weaf-ref, whereas an implicit strong ref makes it hard to break”
I meant it’s easy making strong ref from weak ref but not the other way around.
Thanks for clarifying what you meant by ‘dead’. Reading your post, the impression I got was that you meant the object is dead in the sense of getting GC’ed.
This brings me to another point.
This unpredictability is inherent to everything that uses weakref – such as Threading.Timer. Once ‘dead’ (as per your definition), the timer event may still fire a few times before it gets GC’ed, thus introducing unpredictability. That simply means you need to care about its lifetime and dispose it when you want it to stop. Same principle applies to weak events, you’d still care about its lifetime and remove the event handlers at the correct point. For consistency, I’d prefer all events be changed to weak events by default (should have been this way from the get-go). It’s easy to make a strong ref to an object from a weaf-ref, whereas an implicit strong ref makes it hard to break (in fact, it’ll be impossible if the event is in a third-party library where you wouldn’t want to change the source / haven’t got the source). The overhead to assign and call a weak event if supported natively is negligible. And with events being weak by default, developers are forced to think about the object, as opposed to the method they want triggered, when they hook it to the event.
“This code can continue to get executed long after the object itself is ‘dead’.”
How so? A strong ref would need to have been made prior to the event being called. And once it’s called, the object’s this pointer becomes live and traceable from root. So, GC won’t collect it while it’s being executed.
Hi Zach, I consider an object to be ‘dead’ when the programmer has removed all references to it, and allows it to be GC’ed. For example, if you have a modular UI architecture, you might create a UI module instance, a SaveDialog for example, that the user interacts with. When they have finished with it, you remove your references to it. As you indicated, the object is no longer traceable from root, i.e. there are no stack frames that have reference that reach the object, and at some point in the future it will be garbage collected.
However, if this UI module handles events from an object with a longer lifetime, the ‘application’ object for example, and you do not remove the event handlers when the UI module is no longer used, a strong reference will still exist and it will not be GC’ed at the point you might expect. If you use weak events, the weak reference will mean that this module will be GC’ed if the only reference that exists to it is via the weak event and a memory leak no longer occurs.
However, what if the UI modules event handler performs some non-trivial logic? Perhaps as a result of handling an event from an application it writes to a database, or uses a web service? With a weak event reference this logic could be executed a number of times before the UI module is GCed. This leads to an application which behaves in an unpredictable manner.
I am not against weak events in principle. What I am against is the liberal use of them in instances where all the developer has to do is remove their events handlers at the correct point in the code. If you do so, you will never have event handlers being executed at unpredictable points in time.
Hope that helps,
Colin E.
Thanks Josh … it is only a little bit of code, but I have found so many memory leaks in applications using it
Colin E.
Excellent post, Colin. I think this code belongs in any self-respecting ViewModelBase class.
Josh
Memory leaks happen just as often in Java as the do on the CLR, and for the same reason: strong references being maintained on an object that’s no longer needed. Java’s eventing mechanism is different, but results in the same issues.
This is an interesting diagnostic tool, but it doesn’t solve the problem. The problem is that we’re back into a mode of having to care about object lifetimes. The “dreaded” C++ delete operator is back in another form. The WeakReference approach, while currently much harder to contend with, at least lets us forget about lifetime management and simply let the GC do it’s job.
Perhaps my view of Java is skewed because I mostly worked server-side where events are not nearly so useful. However Java does not posses events as part of the language itself, people must implement the Listener pattern directly. I think this forces people to think a bit more about the impact of adding listeners – there is a bit less magic involved.
I still disagree that the weak reference pattern lets us forget about object lifecycles. It is a dangerous pattern if used without caution, especially when event handlers perform non-trivial logic. This code can continue to get executed long after the object itself is ‘dead’.