Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why will local captured timer not go out of scope? [duplicate]

Possible Duplicate:
Timer, event and garbage collection : am I missing something?

private void TrayForm_Load(object sender, EventArgs e)
{
    System.Timers.Timer HideMeTimer = new System.Timers.Timer(2500);
    HideMeTimer.Elapsed +=  
        delegate(object sender2, System.Timers.ElapsedEventArgs e2)
        {
            this.Invoke(new Action(somestuff));
        };
        HideMeTimer.Start();
        GC.Collect();
}

Can somebody please show me how the compilator will translate this? Or good explanation why the timer keep on ticking after the load event.

(Is not the rule when the last reference is gone, to an variable, the GC will sometime kill the object !)

like image 673
Niklas Avatar asked Nov 22 '11 14:11

Niklas


1 Answers

You need to understand what System.Timers.Timer is doing behind the scenes. It is a wrapper around the System.Threading.Timer class. When the timer starts it creates a new System.Threading.Timer instance and passes it a callback method in the System.Timers.Timer instance. The system references this callback delegate as long as the timer remains enabled.

We also know that delegates keep a reference to an instance of the class containing the target method. This is why your System.Timers.Timer instance is not collected and does not stop automatically. Removing the Elapsed event handler will not solve this problem because that delegate only holds a reference to the TrayForm instance. Again, it is the System.Threading.Timer that is keeping a reference to the System.Timers.Timer. The whole reference chain remains rooted because the system has to reference the System.Threading.Timer for it to work.

Here is the reference chain:

System => _TimerCallback => Callback => System.Threading.Timer => Callback => System.Timers.Timer

When you track this through with Reflector or ILSpy you can see that the "System" in the reference chain above comes into play via the TimerBase.AddTimerNative method which is marked MethodImplOptions.InternalCall so we cannot see exactly how the reference is rooted below this point. But, it is rooted nonetheless.

If you disable the timer via Enabled = false or Stop then it will dispose the underlying System.Threading.Timer which should in turn stop referencing the System.Timers.Timer.

like image 171
Brian Gideon Avatar answered Nov 12 '22 21:11

Brian Gideon