The Inquisitive Coder – Davy Brion's Blog

Trying to walk that thin line between intelligence and ignorance

Why you should always unscubscribe event handlers

Posted by Davy Brion on August 17th, 2008

Events in .NET are very useful. But if you’re not careful, they might prevent objects from being removed from memory by the Garbage Collector (GC). Let’s call an object that publishes an event a Publisher and an object that subscribes to an event a Subscriber. If a Subscriber subscribes to a Publisher’s event, the Publisher will indirectly hold a reference to the Subscriber (because of how events in .NET are implemented). If the Publisher is an object that will stay around for a long time, this could prevent the Subscriber from being removed from memory when it’s no longer used.

Suppose we have the following Publisher class:

    public class Publisher

    {

        public event EventHandler MyEvent;

 

        public void FireEvent()

        {

            if (MyEvent != null)

            {

                MyEvent(this, EventArgs.Empty);

            }

        }

    }

And then we have a BadSubscriber class:

    public class BadSubscriber

    {

        public BadSubscriber(Publisher publisher)

        {

            publisher.MyEvent += publisher_MyEvent;

        }

 

        void publisher_MyEvent(object sender, EventArgs e)

        {

            Console.WriteLine("the publisher notified the bad subscriber of an event");

        }

    }

Notice how the BadSubscriber subscribes to the Publisher’s event, but never unsubscribes from it.

Then we have the GoodSubscriber:

    public class GoodSubscriber : IDisposable

    {

        private readonly Publisher publisher;

 

        public GoodSubscriber(Publisher publisher)

        {

            this.publisher = publisher;

            publisher.MyEvent += publisher_MyEvent;

        }

 

        void publisher_MyEvent(object sender, EventArgs e)

        {

            Console.WriteLine("the publisher notified the good subscriber of an event");

        }

 

        public void Dispose()

        {

            publisher.MyEvent -= publisher_MyEvent;

        }

    }

The GoodSubscriber is very similar to the BadSubscriber, but it unsubscribes from the Publisher’s event when it is disposed. Note that the implementation of the IDisposable interface is hardly ideal, but for the purpose of this demo it’s sufficient.

The following code illustrates the problem:

        static void Main(string[] args)

        {

            var publisher = new Publisher();

            var badSubscriber = new BadSubscriber(publisher);

            var goodSubscriber = new GoodSubscriber(publisher);

 

            var badSubscriberRef = new WeakReference(badSubscriber);

            var goodSubscriberRef = new WeakReference(goodSubscriber);

 

            publisher.FireEvent();

 

            badSubscriber = null;

            goodSubscriber.Dispose();

            goodSubscriber = null;

 

            GC.Collect();

 

            Console.WriteLine(goodSubscriberRef.IsAlive ? "good publisher is alive" : "good publisher is gone");

            Console.WriteLine(badSubscriberRef.IsAlive ? "bad publisher is alive" : "bad publisher is gone");

 

            publisher.FireEvent();

        }

The output of that code is the following:

the publisher notified the bad subscriber of an event
the publisher notified the good subscriber of an event
good publisher is gone
bad publisher is alive
the publisher notified the bad subscriber of an event

Notice how the GoodSubscriber is removed from memory by the GC and how the BadSubscriber is still in memory even though we no longer have a reference to it and the GC has performed a collection.

So keep in mind that every event handler you subscribe to an event should be properly unsubscribed from the event when your instance is no longer needed. Not doing so might lead to instances not being removed if the event’s publisher has a longer lifetime than your object will have. I’d actually suggest you’d always properly unsubscribe from any event you subscribe to. Even if you know that the Publisher won’t have a longer lifetime than your instance will have, who’s to say some future change in the code won’t extend the lifetime of the Publisher over that of the Subscriber? When that happens, you might end up with a bunch of instances that will remain in memory as long as the Publisher remains in memory.

16 Responses to “Why you should always unscubscribe event handlers”

  1. Cherian Thomas Says:

    Do you think it’s wise to make the subscriber register a weak reference?

  2. Davy Brion Says:

    That wouldn’t work with regular events, so you’d have to write custom code to get that working and then you’d have to use that ‘non-standard’ mechanism throughout your code base

    at that point it’s probably easier to just make sure each subscriber offers a way to unsubscribe from the events

  3. David N Says:

    It depends on the context. If object A is listening to object B, yet they both have the same lifetime (i.e. they die together), you don’t need to deregister any events.

    Deregistering is also less trivial when it comes to anonymous delegates.

  4. Davy Brion Says:

    even if they have the same lifetime, if at some point a change is introduced which extends B’s lifetime, A will not be cleaned up and it will keep on receiving the event

  5. Cherian Thomas Says:

    Life becomes horrible if its a static event and you don’t follow these practices :-)

  6. Davy Brion Says:

    everything that’s static has the possibility to become a pain in the ass ;)

  7. David McClelland Says:

    That reminds me of Microsoft’s advice back in the .NET 1.x days for avoiding memory leaks when using WinForms data binding: always call [control].DataBindings.Clear()

    http://msdn.microsoft.com/en-us/library/ms996446.aspx#databinding_winforms10_11_topic11

    Those were the good old days… oh wait! Those days are still here!!!

    :-)

    - David McClelland

  8. David N Says:

    >even if they have the same lifetime, if at some point a change is introduced which extends B’s >lifetime, A will not be cleaned up and it will keep on receiving the event

    But that’s the point. Until that change is introduced, letting the objects die is fine. I have a case right now where I’m creating a request object which creates and then passes control to its child objects. No need to deregister listening to their events, bc when it goes they all go.

    As always, you have to think about context.

  9. Davy Brion Says:

    “Until that change is introduced, letting the objects die is fine.”

    If they are all short-lived objects (for instance, to handle some kind of request like you mention) and their life cycle won’t ever change, then yeah, it’s ok. Although i generally never create short-lived Publishers… i’m sure there are scenario’s where it makes sense but in most cases i think it would just make things more ‘complicated’ than they need to be

  10. David N Says:

    Think about an async request that calls back or publishes some ‘completed’ event. Like you said, a somewhat niche case, but still happens enough that it’s a valid (and simplifying) use.

  11. Davy Brion Says:

    if it’s not in an interactive scenario (UI stuff), i prefer to use waithandles for async stuff though. But your point is definitely valid… in this case it wouldn’t make sense to unsubscribe from the events

  12. Reflective Perspective - Chris Alcock » The Morning Brew #161 Says:

    [...] Why you should always unsubscribe event handlers – Davy Brion talks about one of the most common causes of memory problems caused by the Garbage collector not being able to remove object because other objects still hold references to them via event handlers. [...]

  13. Kiran Says:

    Does this rule apply in UI scenarios? (WinForms/Asp.Net).

    If the answer is yes, do we need to write unsubscription code for every UI element that we subscribe in a form/page? That doesn’t seem normal!

    If the answer is No, and if the responsibility of ‘unsubscribing’ is on subscriber, how the asp.net/winforms publishers are cleaning up the bad subscribers?

  14. Davy Brion Says:

    Not for typical UI scenario’s where the form or the page subscribes to an event of one of the controls that is owned by the form or the page. In that situation, the form/page is the Subscriber and is also the only owner of the Publisher. If the Subscriber is cleaned up, the Publisher automatically goes with it so then there’s no problem. Both instances will be cleaned up without having to explicitely unsubscribe from the events.

    If external objects (instances of other classes or other forms or whatever) subscribe to an event of a Form/Page, then you do have to be pretty careful and you should definitely follow the rule.

  15. Interesting Finds: 2008.08.13~2008.08.21 - gOODiDEA.NET Says:

    [...] Why you should always unscubscribe event handlers [...]

  16. Bradley Grainger Says:

    Coincidentally, we also recently blogged about unsubscribing from events (http://code.logos.com/blog/2008/08/unsubscribing_from_c_events.html) and how to set up a “weak” subscription (which allows the subscriber to be garbage-collected) to any event, without having to modify the event publisher: http://code.logos.com/blog/2008/08/event_subscription_using_weak_references.html.

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>