Mladen Prajdić Blog

Blog about stuff and things and stuff. Mostly about SQL server and .Net

C#: Care about Event Memory Leaks with Delegate.GetInvocationList()

UPDATE:
After i've been proven wrong in the comments below I have to say this:

Go take a read of this article to see how circular references are really handled.

While Delegate.GetInvocationList() really is a great and usefull thing and it could be used in clearing up events,

there's no need for it since you can do that as easily by simply unsubscribin from the event when

you want to dispose your class.

 

You can read this post on,  but know that this example is WRONG, except for the last part about

Observer pattern and WeakEvent pattern.

Maybe you should read this post since sometimes you can learn how stuff works by reading how it doesn't work :))

---------------------------------------------------------------------------------------------------------------------------------------

Subscribed events are one of the most common reasons of memory leaks in .Net. This means that if you have an object that has an event

and there are other object that are subscribed to that event, the original object won't be properly disposed until all events are unsubscribed since

an event is a strong reference.

 

A simple example of this is a form called MyForm that has a public event called OnDoMyFormThing and 5 classes are subscribed to this event.

When the OnDoMyFormThing is executed on the form it will notify all 5 classes. You might call this event a few more times depending on what your form does. 

After this the form is closed. However the form isn't disposed until all 5 classes that are subscribed to the event get disposed. A hard way of doing this

would be to somehow implement the event removal on all 5 classes.

 

An easy way would be to do this in the Dispose method of the form itself. But how, if you don't know the name of every event handler.

But remeber that an event is just a delegate. And delegates have a GetInvocationList() method.

GetInvocationList() gets an array of delegates. Each delegate represents a method subscribed to an event. The order of the delgates is the same as the

invocation order of the subscribed methods on the event.

 

So your code in MyForm would be: 

 public partial class MyForm : Form
 {    
    public event EventHandler OnDoMyFormThing;
<span class="kwrd">public</span> MyForm()
{
    InitializeComponent();        
}

<span class="kwrd">private</span> <span class="kwrd">void</span> button1_OnClick
{
    MessageBox.Show(<span class="str">&#34;Test of OnDoMyFormThing event&#34;</span>);
    OnDoMyFormThing(<span class="kwrd">this</span>, <span class="kwrd">new</span> EventArgs());
}

<span class="kwrd">protected</span> <span class="kwrd">override</span> <span class="kwrd">void</span> Dispose(<span class="kwrd">bool</span> disposing)
{
    <span class="kwrd">if</span> (disposing &amp;&amp; (components != <span class="kwrd">null</span>))
    {
        <span class="kwrd">foreach</span> (EventHandler eventDelegate <span class="kwrd">in</span> OnDoMyFormThing.GetInvocationList())
            OnDoMyFormThing -= eventDelegate;
        components.Dispose();
    }
    <span class="kwrd">base</span>.Dispose(disposing);
}

}

 

And with this your form can be disposed normally.

 

Event subscriptions in 5 classes look like this: 

class1.MyForm.OnDoMyFormThing += Class1EventHandler
class2.MyForm.OnDoMyFormThing += Class2EventHandler
class3.MyForm.OnDoMyFormThing += Class3EventHandler
class4.MyForm.OnDoMyFormThing += Class4EventHandler
class5.MyForm.OnDoMyFormThing += Class5EventHandler

 

The strong bidirectional reference between an event and its subscriptions is a common problem in all Observer pattern scenarions, so you must

know about it and care about it. If not, you're sure to get memory leaks.

 

In .Net ramework 3.0 there is a pattern called WeakEvent that you can use to create a weak reference to events and not really care about disposal.

kick it on DotNetKicks.com
 

 

Legacy Comments


Frank Rau
2007-10-24
re: C#: Care about Event Memory Leaks with Delegate.GetInvocationList()
Does this mean that the CLR GC doesn't finalize the memory references to the form after the application terminates? Or is this only concerning the disposal during the execution of the application?

Mladen
2007-10-24
re: C#: Care about Event Memory Leaks with Delegate.GetInvocationList()
no. yes. :)

when the application terminates the whole AppDomain is unloaded and closed. this means that its memory gets returned to the system.

Sergio
2007-10-25
re: C#: Care about Event Memory Leaks with Delegate.GetInvocationList()
Seems you are wrong.

Subscribing to event increases lifetime of listented but not of the source object. and here is quote from msdn:

...
For instance, in C#, that syntax is: source.SomeEvent += new SomeEventHandler(MyEventHandler).

This technique creates a strong reference from the event source to the event listener. Ordinarily, attaching an event handler for a listener causes the listener to have an object lifetime that influenced by the object lifetime for the source (unless the event handler is explicitly removed).
...

Mladen
2007-10-25
re: C#: Care about Event Memory Leaks with Delegate.GetInvocationList()
could you please post a link to that msdn document you read?

Sergio
2007-10-25
re: C#: Care about Event Memory Leaks with Delegate.GetInvocationList()
sure. WeakEvent :) http://msdn2.microsoft.com/en-us/library/aa970850.aspx

Mladen
2007-10-25
re: C#: Care about Event Memory Leaks with Delegate.GetInvocationList()
hmm... i don't think i understand what you mean then.
where exactly am i wrong?

Sergio
2007-10-25
re: C#: Care about Event Memory Leaks with Delegate.GetInvocationList()
You wrote:
...
This means that if you have an object that has an event and there are other object that are subscribed to that event, the original object won't be properly disposed until all events are unsubscribed since an event is a strong reference.
...

It is not original object wont be disposed but listener. so in example above:

source.SomeEvent += new EventHandler(listener.OnSomeEvent);

listener wont be disposed while source is alive. so if there are no reference to source it will be collected even it has listeners attached, but if no reference to listener and it is subscribed to some of source's event, it will NOT be collected until source is collected.

Mladen
2007-10-25
re: C#: Care about Event Memory Leaks with Delegate.GetInvocationList()
emm... you do realize that the weak event pattern is not what i'm talking about here?
weak event pattern is a new thing in .net 3.0 to prevent bidirectional strong references.

Sergio
2007-10-25
re: C#: Care about Event Memory Leaks with Delegate.GetInvocationList()
I realize. and i don't see bidi reference here. source keeps reference to listener. but listener doesn't keep reference for source in case of event subscription

Mladen
2007-10-25
re: C#: Care about Event Memory Leaks with Delegate.GetInvocationList()
events are delegates. delegates are strong references.
the object that fires the event has a refernce to the object subscribed to that event via that delegate. so they both know about each other until the subscribed object unsubscribes form an event.

this is not a weak event pattern as far as i know.




Sergio
2007-10-25
re: C#: Care about Event Memory Leaks with Delegate.GetInvocationList()
source over delegate has reference to listener - true;
listnener has no reference to source unless you have it with member variable

Sergio
2007-10-25
re: C#: Care about Event Memory Leaks with Delegate.GetInvocationList()
source over delegate has reference to listener - true;
listnener has no reference to source unless you have it with member variable

Mladen
2007-10-25
re: C#: Care about Event Memory Leaks with Delegate.GetInvocationList()
it does have a reference over that delegate.
maybe you should provide some code to let me realize your point easier.
i love to be proven wrong :)

Sergio
2007-10-25
re: C#: Care about Event Memory Leaks with Delegate.GetInvocationList()
http://www.interact-sw.co.uk/iangblog/2004/07/07/circulareventrefs

Sergio
2007-10-25
re: C#: Care about Event Memory Leaks with Delegate.GetInvocationList()
http://www.interact-sw.co.uk/iangblog/2004/07/07/circulareventrefs

Mladen
2007-10-25
re: C#: Care about Event Memory Leaks with Delegate.GetInvocationList()
Very informative read, Sergio!
Thanx!

Sergio
2007-10-25
re: C#: Care about Event Memory Leaks with Delegate.GetInvocationList()
You're welcome:)

eBug
2007-10-25
re: C#: Care about Event Memory Leaks with Delegate.GetInvocationList()
Hi Mladen,

Good post.

You wrote:
foreach (EventHandler eventDelegate in OnDoMyFormThing.GetInvocationList())
OnSearch -= eventDelegate;

What is OnSearch in the foreach loop? Could you be more specific?

Thanks.

Mladen
2007-10-25
re: C#: Care about Event Memory Leaks with Delegate.GetInvocationList()
whoops that's a typo... OnSearch should be OnDoMyFormThing.
i'll fix it.

Jeff Brown
2007-10-26
re: C#: Care about Event Memory Leaks with Delegate.GetInvocationList()
You've got the dependency order wrong as others have pointed out.

Moreover, you could also just set the event handler delegate to null instead of traversing the invocation list. In other words, just write: "OnDoMyFormThing = null".

Mladen
2007-10-26
re: C#: Care about Event Memory Leaks with Delegate.GetInvocationList()
yes. you're right. i'll fix that over the weekend.

JD
2007-10-27
re: C#: Care about Event Memory Leaks with Delegate.GetInvocationList()
Mladen, you're just wrong about the circular references. Save yourself further embarrassment and just delete this blog post. Read Ian Griffiths' article and take comfort in the fact that aren't the first and won't be the last to misunderstand how the .NET GC works, or how circular references are handled in GC environments.

I'd also like to recommend a new problem solving approach for you in the future:

1. First show a simple example that demonstrates the problem that you claim exists (a form and the handler classes NOT being finalized by the GC when fully-dereferenced)
2. Show your supposed fix

If you're going to skip step 1, at least realize that if your solution to the Next Big Problem involves everyone in the world adding code to common classes to fix what you see as a fundamental problem that nobody else has reported, maybe you should take a closer look at how you're writing code to make sure it's not just something you're doing wrong. I guarantee this will save face in the future.

Good luck,

JD

Mladen
2007-10-27
re: C#: Care about Event Memory Leaks with Delegate.GetInvocationList()
yes i know i'm wrong. i thought we came to that conclusion with Sergio already.
thank you for pointing it out :)))

There's no need to save my face, since i'm not the first person to be wrong, right?
For me the main thing is to learn something new which i have.
and no i won't delete the post. i'll just update it with the correct information,
when i came around to itl

shantanu sinha
2007-11-16
c# programming from a to z
Hey

Could you please help me out to find a book which contains the programming basics of C#? The book which can be followed in lab experiments ( from beginners to advanced).
The book which can tell you how to give inputs through console; be it an array, a string or a single character.

Dean
2009-04-22
re: C#: Care about Event Memory Leaks with Delegate.GetInvocationList()
why not just find/replace and do a search for += new in all open documents, then copy paste all event handlers into notepad then paste them into your dispose() method in the .cs designer and replace + with - ?

Sure that would be alot easyier and time consuming than trying to fix something that isnt broken?

Regards

Dean

Luke Puplett
2010-04-02
Thanks
Thank you for keeping this wrong blog post open as the ensuing discussion is very important. The author, by solving a problem that doesn't exist helps to solve a wider problem of misunderstanding in this area.