5

In the following code, I create a DispatcherTimer in the class's constructor. No one keeps reference on it.

In my understanding, the timer should be reclaimed by the garbage collector some time after leaving the constructor's scope. But that doesn't happen! Even after forcing a garbage collection with GC.Collect()

What is going on under the hood?

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        new DispatcherTimer
        {
            Interval = TimeSpan.FromMilliseconds(100),
            IsEnabled = true
        }
        .Tick += (s, e) =>
        {
            textBlock1.Text = DateTime.Now.ToString();
        };
    }
}
Adam Houldsworth
  • 63,413
  • 11
  • 150
  • 187
tpol
  • 910
  • 10
  • 16

3 Answers3

4

Timers are rather special since they root themselves by calling

this.timerRoot = GCHandle.Alloc(this);

some links

EDIT:

Didn't realize that it was a DispatcherTimer - that doesn't root itself directly but indirectly (to the Dispatcher) which in turn leads to the same effect...

Community
  • 1
  • 1
Yahia
  • 69,653
  • 9
  • 115
  • 144
  • Are DispatcherTimers subject to the same garbage collection rules as Threading.Timers? If so, not having a field reference (only a local reference) to the timer would cause garbage collection in and of itself...but I don't think that's the case with DispatcherTimers... – Jeff Aug 31 '11 at 14:25
  • @JeffN825 All objects are subject to the same GC rules: if you're unreachable from a GC root, then you're eligible for GC. In this case, that code isn't even *from* the correct timer class. – dlev Aug 31 '11 at 14:27
  • @dlev just didn't realize it was a DispatcherTimer - see my EDIT – Yahia Aug 31 '11 at 14:29
  • My point was that a locally scoped Threading.Timer with just a local reference is NOT rooted....and thus will be GC'd without ever ticking. – Jeff Aug 31 '11 at 14:37
  • @JeffN825 that is not always true... since a Timer could "root itself" directly or indirectly... I didn't examine the Threading.Timer source (via Reflector) to be sure but AFAIK it calls internally TimerBase.AddTimer which seems to create a reference even for locally scoped Timers... so it too could very well be "rooted indirectly" in your scenario... – Yahia Aug 31 '11 at 15:03
  • I don't believe that's correct. Have a read on the MSDN documentation: http://msdn.microsoft.com/en-us/library/system.timers.timer.aspx – Jeff Aug 31 '11 at 15:08
  • Now that is something different than the Threading.Timer you refered to at first... although I am inclined to check System.Timers.Timer with Reflector... in Reflector you can see that it internally uses Threading.Timer (at least in .NET 4)... so all bets are off... – Yahia Aug 31 '11 at 15:19
3

When you just construct the a DispatcherTimer, nothing prevents it from be GCed. However, you set IsEnabled = true, which will call Start() on the timer. When that, happens, this line of code will be executed:

this._dispatcher.AddTimer(this);

And now the Dispatcher itself is rooting the timer, meaning it can't be GCed.

dlev
  • 48,024
  • 5
  • 125
  • 132
1

It will be added to the Dispatcher queue which will be rooted.

jason
  • 236,483
  • 35
  • 423
  • 525