Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why event handlers prevent garbage collector from occurring

I have this piece of code

public class Publisher
{
    public event EventHandler SomeEvent;
}

public class Subscriber
{
    public static int Count;

    public Subscriber(Publisher publisher)
    {
        publisher.SomeEvent += new EventHandler(publisher_SomeEvent);
    }

    ~Subscriber()
    {
        Subscriber.Count++;
    }

    private void publisher_SomeEvent(object sender, EventArgs e)
    {
        // TODO
    }
}

In the Main method of my application I have

static void Main(string[] args)
{
    Publisher publisher = new Publisher();

    for (int i = 0; i < 10; i++)
    {
        Subscriber subscriber = new Subscriber(publisher);
        subscriber = null;
    }

    GC.Collect();
    GC.WaitForPendingFinalizers();

    Console.WriteLine(Subscriber.Count.ToString());
}

If I run this, I will have 0 as output. If I remove event subscriptions from the code, I will got the expecting result – which is 10.

When GC.Collect() is being called, gc is being forced to start garbage collection. Because Subscriber has Finalize defined in it, GC will suspend collection until finalizequeue is empty – that is after all Subscription instances will call its Finalize() methods ( Please correct me if my assumptions are wrong). At the next line GC.WaitForPendingFinalizers() is called which will effectively suspend execution until finalizer queue is empty. Now, because we have 0 as output I believe Finalize() is not being called, which makes me believe that GC didn’t mark subscriber instances to be collected, thus Finalizer() methods are not being called.

So I have 2 questions

  1. Is my assumption right and event subscription prevents GC to mark subscriber instances to be collected?
  2. If so, it is because publisher holds reference to subscriber? (Garbage collector and event handlers)

My only guess is that since there are 10 instances of Subscriber that are referencing to the same publisher instance, when GC collection occurs, it sees that there are other references to publisher, thus it can’t be collected, and as a result all subscription instances alongside with publisher are being moved to the next generation, so garbage collection doesn’t occur nor Finalize() is being called at the time code execution reaches to Console.WriteLine(Subscriber.Count.ToString())

Am I right or am I missing something here?

like image 624
Michael Avatar asked Jun 18 '13 08:06

Michael


People also ask

Do event listeners get garbage collected?

Yes, an event source won't be garbage collected until it is closed - until then, it may receive events from the network and is kept alive for that.

What is garbage collection Why is it useful when might it be a problem?

Garbage Collection is a form of automatic memory management. It is a special case of resource management, in which the limited resource being managed is memory. Benefits for the programmer is that garbage collection frees the programmer from manually dealing with memory allocation and deallocation.

Should I unsubscribe from events c#?

In order to prevent resource leaks, you should unsubscribe from events before you dispose of a subscriber object. Until you unsubscribe from an event, the multicast delegate that underlies the event in the publishing object has a reference to the delegate that encapsulates the subscriber's event handler.


2 Answers

You are mis-identifying what is really going on, a very common trap in C#. You'll need to run the Release build of your test program and run it without the debugger (press Ctrl+F5). The way it will run on your user's machine. And now notice that it completely doesn't matter anymore whether or not you subscribe the event, you will always get 10.

The issue is that, when you use a debugger, the publisher object doesn't get collected. I explained the reason for that in detail in this answer.

Expanding a bit on that, you have circular references here. The Subscriber objects reference the Publisher object. And the Publisher object has references to the Subscriber objects. Circular references are not sufficient to keep objects alive. And thank goodness for that, garbage collection would not be very effective if that was the case. The publisher object must be referenced elsewhere to stay alive, the local variable isn't good enough.

like image 127
Hans Passant Avatar answered Oct 20 '22 01:10

Hans Passant


The answer to both questions is Yes.

like image 26
Henrik Avatar answered Oct 20 '22 01:10

Henrik