Why does a System.Timers.Timer survive GC but not System.Threading.Timer?
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
The short answer is that System.Threading.Timer does not keep itself alive. If you create one and then drop every strong reference, the garbage collector is free to reclaim it even though the native timer queue still knows about the callback. System.Timers.Timer often appears to "survive" longer, but that is usually because user code or a component container is still holding a reference, not because the GC treats it as special.
Why System.Threading.Timer Gets Collected
System.Threading.Timer is a lightweight wrapper around timer infrastructure used by the runtime. The important detail is that unmanaged timer registration is not a managed GC root.
That means this code is unsafe:
Because timer is a local variable, it becomes unreachable after StartTimer() returns. Once that happens, the runtime is allowed to collect it and the callbacks may stop.
The fix is simple: keep a strong reference for as long as the timer should run.
Why System.Timers.Timer Often Looks Different
System.Timers.Timer is a higher-level wrapper that raises an Elapsed event. In real code it is frequently stored in a field, held by a component container, or attached to a long-lived object. Those normal usage patterns create strong references that keep it alive.
In this example, the Worker instance references the timer through the _timer field, so the timer stays reachable as long as the Worker stays reachable.
That is not a GC exception. It is ordinary object reachability.
Do Not Read Too Much Into the Event Model
A common misunderstanding is that the Elapsed event itself keeps the timer alive. The event subscription does not create a magical root. The timer holds a reference to the delegate target, not the other way around.
So this is still fragile if the timer itself is not rooted:
After StartTimer() returns, there may be no strong reference to timer. Whether it appears to keep firing for a while is an implementation detail and a timing accident, not behavior you should rely on.
The Practical Rule
Treat both timer types the same from a lifetime perspective:
- keep a strong reference while the timer should be active
- dispose the timer when you are done
- do not assume callbacks imply reachability
If you really must use a short-lived local reference in an edge case, GC.KeepAlive() can extend the lifetime to a precise point in the method.
Common Pitfalls
- Creating a
System.Threading.Timerin a local variable and assuming the callback keeps it alive. - Concluding that
System.Timers.Timeris GC-proof because it happened to survive in one test. - Forgetting to dispose timers, which can leave callbacks running longer than intended.
- Assuming event subscription creates a strong reference back to the timer itself.
- Relying on observed timing instead of explicit object lifetime management.
Summary
- '
System.Threading.Timercan be collected as soon as no strong reference remains.' - '
System.Timers.Timeroften survives only because user code usually stores it in a field or container.' - Event handlers do not automatically root the timer.
- Keep a strong reference to either timer type for as long as you need it.
- Dispose the timer explicitly when its work is finished.
Related reading
- Why does an async single task run faster than a normal single task?
- Why does Async.Start seemingly propagate uncatchable exceptions?
- Why Does Await Not Appear to Prevent Second Operation on EF Context
- Why does Exception from async void crash the app but from async Task is swallowed
- Why does Boolean.ToString output True and not true
- Why does C execute Math.Sqrt more slowly than VB.NET?
- Why does holding a non-Send type across an await point result in a non-Send Future?
- Why does Interlocked.Exchange not support Boolean type?

OOD Fundamentals
Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.