Can Timers Get Automatically Garbage Collected

Can Timers get automatically garbage collected?

This is only a problem with the System.Threading.Timer class if you don't otherwise store a reference to it somewhere. It has several constructor overloads, the ones that take the state object are important. The CLR pays attention to that state object. As long as it is referenced somewhere, the CLR keeps the timer in its timer queue and the timer object won't get garbage collected. Most programmers will not use that state object, the MSDN article certainly doesn't explain its role.

System.Timers.Timer is a wrapper for the System.Threading.Timer class, making it easier to use. In particular, it will use that state object and keep a reference to it as long as the timer is enabled.

Note that in your case, the timer's Enabled property is false when it enters your Elapsed event handler because you have AutoReset = false. So the timer is eligible for collection as soon as it enters your event handler. But you stay out of trouble by referencing the sender argument, required to set Enabled back to true. Which makes the jitter report the reference so you don't have a problem.

Do be careful with the Elapsed event handler. Any exception thrown inside that method will be swallowed without a diagnostic. Which also means that you won't set Enabled back to true. You must use try/catch to do something reasonable. If you are not going to intentionally end your program, at a minimum you'll need to let your main program know that something isn't working anymore. Putting Enabled = true in a finally clause can avoid getting the timer garbage collected, but at the risk of having your program throw exceptions over and over again.

How to prevent System.Threading.Timer get garbage collected in UWP?

The timer has a reference to the object that defines UndoDeleteTimerFinish. If that object's only refrence is to the Timer and vice-versa they will both be eligible for garbage collection.

Quick and dirty fix is to have an object that is not garbage collected hold a reference to your Timer.

class ObjectNotGettingGarbageCollected{
Timer _timer; //won't be GC'd

SomeMethod(){
_timer = new Timer(UndoDeleteTimerFinish, email, UndoBannerDisappearTime, Timeout.Infinite)
}
}

Also as long as the state object (email in you case) is referenced somewhere, the CLR keeps the timer in its timer queue and the timer object won't get garbage collected. So I would check what is the status of the 'email' object which you pass as the state parameter to your Timer.

More about this here:

The System.Threading.Timer constructor has several overloads; all except one take a state parameter which is passed to the TimerCallback delegate when the timer fires.

It turns out that this state parameter (and the TimerCallback delegate) have an interesting effect on garbage collection: if neither of them reference the System.Threading.Timer object, it may be garbage collected, causing it to stop. This is because both the TimerCallback delegate and the state parameter are wrapped into a GCHandle. If neither of them reference the timer object, it may be eligible for GC, freeing the GCHandle from its finalizer.

The single-parameter constructor does not suffer from this problem, because it passes this for the state (not null). Most real-world usage of System.Threading.Timer either references the timer from the callback or uses the timer for the state, so this interesting garbage collection behavior will probably not be noticed.

http://blog.stephencleary.com/2011/07/systemthreadingtimer-constructor-and.html

Why doesn't the timer get garbage collected?

When you create a Timer, internally a TimerQueueTimer is created based on your Timer. The Timer has a reference to this so that you can continue to modify and control the timer.

The static field s_queue of the System.Threading.TimerQueue class (Source) holds a reference to an active TimerQueueTimer (it's indirect, there's yet another wrapper class) even after you forget the reference to the Timer which created it.

If you examine the source in that linked file for the Timer constructor and its change method you'll see that during change a reference to a TimerQueueTimer (indirectly, the queue is of another wrapper class) does get stored in TimerQueue.s_queue.

C# Timers and Garbage Collection

Each Timer references a TimerHolder, which references a TimerQueueTimer. The implementation keeps an internal reference to the TimerQueueTimer via the call to UpdateTimer().

Under normal circumstances your timer is free to be collected, finalizing the TimerHolder and removing the TimerQueueTimer from the internal queue. But the simple constructor Timer(TimerCallback) calls TimerSetup() with the Timer itself as the state. So in this particular case, the TimerQueueTimer's state references back to the Timer, preventing it from being collected.

The effect is not related to keeping a temporary local variable. It just so happens to work due to the internals of the Timer mechanism. It is much clearer and safer to keep a reference to your timer as recommended by MSDN.

C# Garbage collection of object that has a running timer

In this case it seems there will indeed be a leak if you won't Dispose your Test, and finalizer won't help (at least all my tests show that this is how it will behave).

As stated here - timer internally registers callback in GCHandle table, which prevents situation that callback might be invalid when it's time to fire it. Because your callback (Timer_Elapsed) is not static - that means it holds reference to this and so your Test object cannot be collected, because there is rooted reference (GCHandle) to your callback. Now, your object in turn has reference to timer, because you store it in a field. So after all, there is reference from root to both your Test object and your timer, and neither of them can be garbage collected.

To fix the issue - you need to break some of the strong references. You can do that for example like this:

public class Test : IDisposable {
protected readonly Timer Timer;
protected int Count = 0;
private bool disposed = false;

public Test() {
Timer = new Timer(Timer_Elapsed, new WeakReference<Test>(this), 0, 1000);
}

private static void Timer_Elapsed(object state) {
var wr = (WeakReference<Test>) state;
Test test;
if (wr.TryGetTarget(out test)) {
test.Count++;
Console.WriteLine(test.Count);
}
}

public void Dispose() // implementing IDisposable
{
if (!disposed) {
Timer.Dispose();
disposed = true;
}
}
}

There we first replace System.Timers.Timer with System.Threading.Timer and also make callback method static, so that it no longer prevent parent Test object from collection. However, we need to access Test object somehow in callback - so we pass WeakReference as a state there (we cannot pass object itself because it will create the same situation we are trying to avoid). In callback we check if Test object is still alive, and if yes - do what is needed with it.

We also remove destructor because it's not necessary here. When Test object is garbage collected - Timer will also be collected and it's own destructor will be called.

Timers and Garbage Collection

  1. Generally correct. When you dispose Timer, ObjA will be eligible for GC. In fact, garbage collector will collect it during its next garbage collection cycle.

Keep in mind that, It will not collect your object immediately after it becomes eligible for GC. Garbage collector uses its heuristic algorithm to to trigger garbage collection. It occurs only when there is a memory pressure. In fact when Gen0 or Large Object Heap about to overflow.


  1. Possibly.. Have a look at here.

Callbacks can occur after the Dispose() method overload has been
called, because the timer queues callbacks for execution by thread
pool threads. You can use the Dispose(WaitHandle) method overload to
wait until all callbacks have completed.

Why does a System.Timers.Timer survive GC but not System.Threading.Timer?

You can answer this and similar questions with windbg, sos, and !gcroot

0:008> !gcroot -nostacks 0000000002354160
DOMAIN(00000000002FE6A0):HANDLE(Strong):241320:Root:00000000023541a8(System.Thre
ading._TimerCallback)->
00000000023540c8(System.Threading.TimerCallback)->
0000000002354050(System.Timers.Timer)->
0000000002354160(System.Threading.Timer)
0:008>

In both cases, the native timer has to prevent GC of the callback object (via a GCHandle). The difference is that in the case of System.Timers.Timer the callback references the System.Timers.Timer object (which is implemented internally using a System.Threading.Timer)

Will this get Garbage Collected?

Yes, the callback will be eligible for garbage collection after the callback has executed once. This is a reasonable way of subscribing to an event in order to execute the handler just once.

It's tough to prove that the delegate will be garbage collected - or indeed that the object used to hold the captured variables callback and timer will be garbage collected, admittedly... you'd really want to put a finalizer there (which affects garbage collection itself, of course), but you can't as it's generated code.

System.Windows.Forms.Timer and Garbage Collection with C#

Theory

If object A holds a reference to object B, and you assign null to A, then after a while A is garbage collected. if there are still others with a reference to B, then B is not garbage collected. However, if A was the last one with a reference to B, then B will also be collected:

Person Joseph = new Person
{
Id = 1,
Address = new Address {Id = 10, ...},
...
};

Joseph.Address = new Address {Id = 11, ...}

After a while Address with Id 10 will be garbage collected, because no one holds a reference to it anymore.

Person Mary = new Person
{
Id = 2,
Address = Joseph.Address,
...
};
Joseph = null;

After a while the Person with Id 1 will be garbage collected, because no one holds a reference to it anymore. However, the Address with Id 11 will not be garbage collected, because the Person with Id 2 holds a reference to it.

mary = null;

Now no one holds a reference to the Address with Id 11, so it will be garbage collected.

Back to your question

  • You create a new Container object
  • You create a new NotifyIcon object.
  • You add this NotifyIcon object to the components object
  • You create a new Timer objects, using the constructor that orders the timer to add itself to the Container.

So the Container object has references to both the created NotifyIcon and Timer objects. As long as these objects are not Removed from the Container, and the container is not Disposed nor garbage collected, this Container will hold these references, and thus neither the notifyIcon nor the Timer will be garbage collected.

components.Dispose();

According to the reference source of System.ComponentModels.Container this will do the following:

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
lock (syncObj) {
while (siteCount > 0)
{
ISite site = sites[--siteCount];
site.Component.Site = null;
site.Component.Dispose();
}
sites = null;
components = null;
}
}

So Container.Dispose() will call Dispose() of all added components, and then release the reference to the added components.

  • If you have references to these components elsewhere, then these components will not be garbage collected. However, since they are Disposed, usually they are not useable.
  • If the Container was the only one with references, then after Container.Dispose() the NotifyIcon and the Timer are eligible to be collected.
  • Because you still hold a reference to the Container, the Container itself is not collected. Since it is Disposed, the Container cannot be used anymore.
  • To make sure that the Container is collected, either let the reference go out of scope, or assign null to it.

In every Form.Dispose() you'll find code similar to:

private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}

When the Form is Disposed, this.components is Disposed also, meaning that all IComponents that it holds are Disposed, and the references are removed. Even the reference to Container.ComponentCollection is removed. Note that the form still holds a reference to this.components, so even though this.components cannot be used anymore.

This latter is a bit strange: if you don't hold references to your NotifyIcon / Timer anymore they are garbage collected, however this.Components is not, as long as the form exists. It would have been neater if Form.Dispose also released the reference to this.components



Related Topics



Leave a reply



Submit