When are method variables, accessible in an anonymous method, garbage collected?

1.3k views Asked by At

For instance is it necessary to add a Timer instance to a list as I am doing here to prevent the Timer being garbage collected? Tyically if the callback was not anonymous the aswer is yes, but since it is anonymous i imagine the variables in the method block which are accessible in the anonymous method block will only be garbage collected when the anonymous method completes? In which case no need to save ref as I am doing..:

private static List<Timer> timers = new List<Timer>();

public static void RunCallbackAfter(Action callback, int secondsToWait)
{
        Timer t = null;
        t = new Timer(new TimerCallback(delegate(object state)
            {
                SomeThread.BeginInvoke(callback);
                timers.Remove(t);
            }), null, secondsToWait*1000, Timeout.Infinite);
        timers.Add(t);
}
3

There are 3 answers

0
Jon Skeet On BEST ANSWER

The objects referred to by the captured variables in the anonymous method will not be eligible for garbage collection until the delegate created by the anonymous method is eligible for garbage collection.

However, if it's only the Timer which has a reference to the delegate, and nothing else has a reference to the timer, then I suspect both would be eligible for garbage collection, assuming this is indeed the kind of timer which you need to keep a reference to. (I seem to remember that some timers do require this and some don't. I can't remember which is which.)

Also, if you removed the timers.Remove(t) call from within the anonymous method then it wouldn't be capturing t in the first place. It's only captured variables which have prolonged lifetimes... not every variable in the method which contains the anonymous method.

2
Daren Thomas On

No. You don't need to stash away your timer. It would normally be garbage collected, since it is only referenced by your delegate. However, I believe that the Timer constructor places a reference with the underlying runtime, so you should be fine.

Raymond Chen probably has something to say in his blog post: The implementation of anonymous methods in C# and its consequences (part 1)

Your Timer variable t will stay accessible as long as there is a reference to your anonymous method. Your anonymous method will stay referenced until the event has fired. At least that long.

According to Raymond Chen, the c# compiler turns your code into something else, with the context of your method (including the this pointer of the enclosing object) all wrapped up in its own little compiler generated class. So it seems the anonymous method (or delegate) itself contains the reference to the timer.

Oh, and everyone else in this thread are correct: I just blurted out some stuff (and learned about how the C# compiler handles anonymous methods at the same time).

So yes, you have a circular reference. But I'm pretty sure creating a timer should hook that up somewhere in the runtime / windows. Let's have a check.

Using Reflector, you can follow the trail from System.Timer.Timer() through to TimerBase, which has an extern method AddTimerNative. I'm not sure how to peek into that, but I'm going to bet it registers your timer with the OS.

Conclusion: Your timer will not go out of scope, as it will be referenced by the OS until it fires.

0
bdonlan On

In general, anonymous functions in C# will keep any local variables that they reference alive until the anon function itself is destroyed. Naturally, removing the remove call in this case would remove the reference, meaning the variable would no longer be kept alive by the callback.

However, this here forms a circular reference; unless there's an outside reference the timer and callback could be destroyed simultaneously. I'm not sure if starting a timer counts as an external reference keeping it alive in C#, so I can't answer your question fully, though. If a started timer is treated as having an external reference, then that alone would keep both the timer and callback alive.