Babak Golkar
Babak Golkar

Reputation: 169

Event handler affects in garbage collection in CLR

I'm completely confused about how event handler can affect in garbage collection operation.

For example, why object a1 not collected by garbage collection ( the destructor of a1 is not calling):

Even after unsubscribing timeChange eventHandler the destructor isn't called by garbage collector.

Best regards.

public class B
{
    private void button1_Click(object sender, EventArgs e)
    {    
        A a1 = new A();
        a1.timeChange += A1_timeChange;

        a1.Start();

        a1 = null;

        GC.Collect();
    }

    private void A1_timeChange(object sender, EventArgs e)
    {
        MessageBox.Show(((DateTime)sender).ToString() );
    }
}

public class A
{
    ~A()
    {
        MessageBox.Show("A Collected");
    }

    public void Start()
    {
        if (timeChange != null)
        {
            Task.Factory.StartNew(() => {
                while (true)
                {
                    timeChange(DateTime.Now, null);
                    System.Threading.Thread.Sleep(3000);
                }
            });
        }
    }
    public event EventHandler timeChange;
}

Upvotes: 3

Views: 354

Answers (1)

ironstone13
ironstone13

Reputation: 3453

In summary
It is not the event itself that is causing this, but rather referencing an instance member of class A from a long running thread using a closure.

The event itself is not the issue, or is it?
This code a1.timeChange += A1_timeChange; causes a delegate inside class A that implements the event public event EventHandler timeChange to reference A1_timeChange inside class B.
So, the reference is the other way around
In your scenario, if you got rid of all references to class B, but did not unsubscribe from the event, then the event handler in A that points to a handler method in B could keep class B reachable, and therefore not GC'ed

What is really happening
Your class A is accessible from one of the GC roots (specifically, actively running threads), it's still reachable and therefore, not collected - read more

You have spawned an eternally running task (well it will stop when the app closes / foreground thread terminates) with this code

Task.Factory.StartNew(() => {
                while (true)
                {
                    timeChange(DateTime.Now, null);
                    System.Threading.Thread.Sleep(3000);
                }

The thing is, that you're using a lambda that closes over the timeChange event, and that makes a compiler to generate a class that simply references class A

One more thing, just by having a descructor (which is compiled to a finalizer) you prolong the lifetime of your object by one GC collection - on the first GC, your object will be marked as unreachable, and will be put onto finalization queue. And on the next GC, the finalizer will actually run - read more

Upvotes: 1

Related Questions