Click here to Skip to main content
15,867,324 members
Articles / Programming Languages / C#
Article

Event Handling and Garbage Collection in .NET

Rate me:
Please Sign up or sign in to vote.
4.85/5 (13 votes)
18 Nov 2009CPOL8 min read 47.3K   484   42   5
An article that looks at the interplay between Event Handling and Garbage Collection

Screenshot of application when first started

Screenshot of application when run in 'Do Nothing' mode

Screenshot of application when run in 'Dispose' mode

Screenshot of application when run in 'Remove Event Handler' mode

Introduction

This article discusses some of the interplay between two important features of .NET, Event Handling and Garbage Collection. It examines how the use of events and event handlers helps determine an object's lifetime. The article also looks at what the potential repercussions are when two objects have a mutual dependency of event handling, i.e. they each have an event and also a handler attached to the other's event. Two strategies are considered for how this mutual dependency should be handled and a recommendation given as to which one is the preferred solution.

This article assumes that the reader is familiar with the Event Handling and Garbage Collection in .NET.

The code sample with this article is C#, but the points are as relevant to VB.NET.

Background

Event Handling allows software to implement asynchronous behaviour. The objects that raise the event and the objects that react to it are isolated from each other and can be designed and built independently. It is possible for two objects to form a mutual event handling relationship with each attaching an event handler to the other's event. However, implementing a relationship like this can have unintended consequences both for how the event handling behaves and the lifetime of the objects concerned in terms of when the objects are deemed to be unreferenced by the Garbage Collector and eligible to be removed from the managed heap.

I became interested in this subject when some code I was working on exhibited some unexpected behaviour. It took me quite a while to puzzle out what was happening when event handlers were being called more times than they should have been and objects that I thought had been unreferenced, and hence no longer active, were very much alive and kicking. The code samples I give with this article reflect what I had in real life in terms of essential structure, but stripped down to what is relevant to the point in hand. I have also added a couple of embellishments where necessary to help identify what is happening.

The Code

The sample code is a simple Windows Form application. There are two main classes, Form1, which is the form itself, and the EventGenerator class. Both Form1 and EventGenerator have an event member and a method that can be attached to the other's event to handle it. EventGenerator has a private _string member which is set to the value of the string passed into the constructor. This _string acts as a signature to uniquely identify every instance of EventGenerator.

Here is the code for Form1.

C#
public partial class Form1 : Form
    {
        private EventGenerator _eventGenerator;
        private int _counter;   //keeps track of how many EventGenerator objects created

        public delegate void FormEventHandler(object o, EventArgs e);
        public event FormEventHandler FormEvent;

        public Form1()
        {
            InitializeComponent();
        }

        private void handlerMethod(object o, EventArgs e)
        {
            listBox1.Items.Add("handlerMethod Called: " + e.ToString());

        }

        private void Generate_Click(object sender, EventArgs e)
        {
            //Decide what to do about cleaning up existing reference to _eventGenerator
            //before creating new one
            if (_eventGenerator != null)
            {
                if (radioRemove.Checked)
                    FormEvent -= _eventGenerator.theFormHandlerMethod;
                else if (radioDispose.Checked)
                    _eventGenerator.Dispose();
                //if radioDoNothing checked then do nothing
            }

            listBox1.Items.Clear();

            _eventGenerator = new EventGenerator
		("Creating eventGenerator: " + _counter.ToString());
            ++_counter;

            FormEvent += _eventGenerator.theFormHandlerMethod;
            _eventGenerator.Event += handlerMethod;
            FormEvent(null, null);

            foreach (string s in FinalizeRegister.Messages)
            {
                listBox1.Items.Add(s);
            }
        }
    }

The code for EventGenerator is here:

C#
public sealed class  EventGenerator : IDisposable
    {
        public delegate void EventHandler(object o, EventArgs e);
        public event EventHandler Event;
        private string _string;


        public EventGenerator(string s)
        {
            _string = s;
        }

         ~EventGenerator()
        {
            FinalizeRegister.AddMessage("Finalized called for " + _string);
        }

        public void theFormHandlerMethod(object o, EventArgs e)
        {
            //Don't raise an event if no subscribers
            if (Event != null)
                Event(null, new DerivedEventArgs(_string));
        }

        /// <summary>
        /// Not the way to do things, as we shall see!
        /// </summary>
        public void Dispose()
        {
            Event = null;
        }
    }

There are also two additional classes:

DerivedEventArgs, is as you might think, derived from EventArgs, it has a string as a private member which is set in its constructor. Its inherited 'ToString' method is overridden to return the value of this string member.

C#
public sealed class DerivedEventArgs : EventArgs
    {
        private string _string;

        public DerivedEventArgs(string s)
        {
            _string = s;
        }

        public override string ToString()
        {
            return _string;
        }
    }

The FinalizeRegister is a static class that wraps a list of strings. It has an 'AddMessage' method to add a string to the list and a read only 'Messages' property to return the list.

C#
public static class FinalizeRegister
    {
        private static List<string> _messages = new List<string>();

        public static void AddMessage(string s)
        {
            _messages.Add(s);
        }

        public static List<string> Messages
        {
            get { return _messages; }
        }
    }

EventGenerator implements a Finalizer. In the Finalizer, it adds its _string member to the FinalizeRegister.

Running The Software

If you ran the software, you would see that the Form looks like this:

Screenshot of application when first started

The code behind the 'Generate' button is like this:

C#
private void Generate_Click(object sender, EventArgs e)
    {
        //Decide what to do about cleaning up existing reference to _eventGenerator
        //before creating new one
        if (_eventGenerator != null)
        {
            if (radioRemove.Checked)
                FormEvent -= _eventGenerator.theFormHandlerMethod;
            else if (radioDispose.Checked)
                _eventGenerator.Dispose();
            //if radioDoNothing checked then do nothing
        }
        
        listBox1.Items.Clear();
        
        _eventGenerator = new EventGenerator
		("Creating eventGenerator: " + _counter.ToString());
        ++_counter;
        
        FormEvent += _eventGenerator.theFormHandlerMethod;
        _eventGenerator.Event += handlerMethod;
        FormEvent(null, null);
        
        foreach (string s in FinalizeRegister.Messages)
        {
            listBox1.Items.Add(s);
        }
    }

Ignore the first few lines that deal with the state of the Program Mode radio buttons for the moment. The code does the following:

  • Clears the contents of the ListBox.
  • Creates a new instance of the EventGenerator class and assigns it to the _eventGenerator reference.
  • The EventGenerator constructor is passed a string which will be unique because it contains the value of the _counter variable which is incremented each time this method is run.
  • _eventGenerator's 'theFormHandlerMethod' is attached to the Form1's 'FormEvent' event.
  • Form1's 'handlerMethod' is attached to _eventGenerator's Event method.
  • The FormEvent method is raised.
  • Every message in the FinalizeRegister is added to the ListBox.

An important point to note is that when a new EventGenerator is assigned to _eventGenerator, the previous instance of EventGenerator is no longer referenced. One might expect it to be no longer active and to be sitting quietly for the Garbage Collector to clean it up. As we will see later, this is not the case.

Let's have a look at how the event handling works. As we have seen, Form1's event is raised when the 'Generate' button of the form is clicked. The EventGenerator handles this with theFormHandlerMethod.

C#
public void theFormHandlerMethod(object o, EventArgs e)
   {
       //Don't raise an event if no subscribers
       if (Event != null)
           Event(null, new DerivedEventArgs(_string));
   }

It checks to see if handlers are attached to the Event method (an important check as you will get a NullException thrown if there are no attached handlers) and then raises its own Event. When it raises the Event, it creates an instance of DerivedEventArgs passing in its own private _string member as a parameter to the constructor.

The control will then pass back to Form1 which will handle EventGenerator's Event here:

C#
private void handlerMethod(object o, EventArgs e)
    {
        listBox1.Items.Add("handlerMethod Called: " + e.ToString());
    }

The EventArg's ToString method is called and added to the ListBox control. This will be the string which uniquely identifies the instance of the EventGenerator.

If we run the Form and click generate a few times, we should get something like this:

Screenshot of application when run in 'Do Nothing' mode

Remember that each time you click 'Generate', the ListBox is cleared. You might expect that if you had clicked the button five times only, the fifth EventGenerator object to be active and only 'handlerMethod called: Creating eventGenerator: 5' to be in the ListBox. The first four instances of EventGenerator should be no longer active as they are no longer referenced by Form1's _eventGenerator reference. But, what this code shows is that not only are these instances still active, but they are also handling Form1's event to which they have a handler attached. Although these objects are no longer referenced, the fact that they have handlers still attached to the vent of an active object, Form1, means that they themselves are still active. As they are active, they can still handle events that they are attached to and they will not be Garbage Collected.

This is probably not what we expect or what we want!

A couple of solutions to this are examined:

Solution 1 - Breaking the Event Handling on EventGenerator's Side

EventGenerator implements the IDisposable interface. Anything that implements IDisposable has a 'Dispose' method that allows a bit of tidying up to occur and resources to be freed. EventGenerator's 'Dispose' is like this:

C#
public void Dispose()
 {
     Event = null;
 }

The Event is set to null.

Look again at the first lines of code behind Form1's Generate button:

C#
//Decide what to do about cleaning up existing reference to _eventGenerator
//before creating new one
if (_eventGenerator != null)
{
    if (radioRemove.Checked)
        FormEvent -= _eventGenerator.theFormHandlerMethod;
    else if (radioDispose.Checked)
        _eventGenerator.Dispose();
    //if radioDoNothing checked then do nothing
}

As you can see, if the 'Dispose' radio button in the form is selected then _eventGenerator's Dispose method is called before the reference is reused on a new instance of EventGenerator.

Run the application again, but this time select the 'Dispose' radio button. Click the 'Generate' button a few times and you will get something like this.

Screenshot of application when run in 'Dispose' mode

As you can see, Form1 is now only handling the event generated by the most recent instance of EventGenerator. All the previous instances are silent. This is nearer to what we want, but, and this is a big but, the code is still wrong. Remember that EventGenerator has a Finalizer in which it adds its unique string to the FinalizeRegister object. Form1 will add the contents of FinalizeRegister to the ListBox each time 'Generate' is clicked. However many times you click 'Generate' when the application is in 'Dispose' mode the unreferenced EventGenerator objects are not having their 'Finalize' methods called and, hence, we know that they are not being Garbage Collected. This could be quite serious if the memory they consume on the managed heap is never released. This is not what we want!

Solution 2: Breaking event handling on Form1's Side

If the 'Remove Event Handler' radio button is selected, then Form1's event handler is removed from _eventGenerator's event.

Run the application again with 'Remove Event Handler' selected.

Screenshot of application when run in 'Remove Event Handler' mode

Click 'Generate' for a while and you will see that only the current EventGenerator object is raising events, which is what you want, and that eventually the Garbage Collector steps in and cleans up the unreferenced EventGenerator objects. Note that Finalizers are called in no particular order, which illustrates the indeterminate nature of Garbage Collection.

It is only by removing all the event handlers attached to an object's events that object is considered by the Garbage Collector to be unreferenced.

Conclusion

A mutual dependence between objects for Event Handling can have unintended consequences. Even when an instance of an object has no references to it, it will still handle any events on which it itself is attached. It will also not be a candidate for Garbage Collection. Breaking the dependency on the side of the referenced object is only a partial solution. Removing event handlers attached to events on the object that is about to be unreferenced is the correct approach. Not only will these objects no longer handle events they will also be Garbage Collected. May they Rest In Peace!

History

  • First draft: 14th November 2009

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Team Leader
United Kingdom United Kingdom
David has been developing for a number of years, first in C++, and more recently on .NET. Having lived in London for several years where he worked on share trading systems in the early days of electronic trading, David has now returned to his home city of Sheffield and is developing software for the rail industry.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Avi Farah5-Jan-13 22:42
Avi Farah5-Jan-13 22:42 
QuestionRecomended pattern for disposing of events Pin
Lazar Videnov30-Nov-09 5:13
professionalLazar Videnov30-Nov-09 5:13 
AnswerRe: Recomended pattern for disposing of events Pin
David J Parsons30-Nov-09 6:30
David J Parsons30-Nov-09 6:30 
GeneralYup, can cause huge problems in ASP.NET Pin
MR_SAM_PIPER23-Nov-09 15:02
MR_SAM_PIPER23-Nov-09 15:02 
General5 Stars for you my friend :D Pin
Midnight Run19-Nov-09 5:46
Midnight Run19-Nov-09 5:46 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.