SteveM
SteveM

Reputation: 305

Lambda closure lifetime and timer handlers

I normally avoid using local variables to hold references to timers because when the local variable goes out of scope the timer gets garbage collected and stops firing. However, I ran into a bit of code that seems to get around this problem by referencing the timer itself through a closure in a lambda expression that is attached to the Elapsed event.

{
    var bkgTimer = new System.Timers.Timer(timeDelay) {AutoReset = false};
    bkgTimer.Elapsed += (sender, args) => Handler(e, bkgTimer);
    bkgTimer.Start();
}

Then later, in the Handler:

private void Handler( ... timer)
{
    ...
    timer.Dispose();
}

It appears to work. Are there any problems with this approach? I wondered if it could lead to a memory leak since I don't understand what the lifetime of the lambda closure is controlled by.

Upvotes: 0

Views: 2165

Answers (1)

Marcelo Cantos
Marcelo Cantos

Reputation: 185902

An object cannot avoid GC simply by referencing itself — that's the key distinction between true garbage collectors and reference-counting techniques. If the GC cannot reach it from a root, it will collect it.

Call GCHandle.Alloc to prevent collection, and call GCHandle.Free in the Handler method to prevent leakage.

The advice in the documentation to use GC.KeepAlive only applies to long-running methods. If the method exits immediately, as yours appears to do, GC.KeepAlive won't have the desired effect.

Incidentally, you shouldn't need a separate handler method:

var bkgTimer = new System.Timers.Timer(timeDelay) {AutoReset = false};
var timerHandle = GCHandle.Alloc(bkgTimer);
bkgTimer.Elapsed += (sender, args) => {
    …
    timerHandle.Free();
    bkgTimer.Dispose();
};

bkgTimer.Start();

Upvotes: 2

Related Questions