6

My question is simple, why GC can't figure it out that timer object in the main should be garbage collected along with the timer inside TestTimer and associated EventHandler?

Why am I continously getting console.Writeline output?

class Program
{
    public static void Main()
    {       
       TestTimer timer = new  TestTimer();
       timer = null;
       GC.Collect();
       GC.WaitForPendingFinalizers();
       Console.ReadKey();
    }
}

public class TestTimer
{
    private Timer timer;

    public TestTimer()
    {
        timer = new Timer(1000);
        timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
        timer.Start();
    }

    private void timer_Elapsed(Object sender, ElapsedEventArgs args)
    {
        Console.Write("\n" + DateTime.Now);
    }
}
Jalal Said
  • 15,906
  • 7
  • 45
  • 68
Haris Hasan
  • 29,856
  • 10
  • 92
  • 122
  • 1
    GC is not permanently scanning bytecode. that would be huge disadvantage. depending on strategy, it only scans depending on heap size or is timer based. – fazo Jul 23 '11 at 10:41

5 Answers5

7

Don't depend on the GC, use the Dispose pattern to properly dispose the TestTimer (which then should dispose the Timer).

However, what happens is that the timer keeps itself alive by getting a GC handle on itself. Read this blog post:

http://nitoprograms.blogspot.com/2011/07/systemthreadingtimer-constructor-and.html?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+blogspot%2FOlZtT+%28Nito+Programming%29

Lucero
  • 59,176
  • 9
  • 122
  • 152
  • I have read that you should only use Dispose pattern in case of Database connections or file handled or things like that and it is not a good practice to use it against everyday .Net objects. otherwise what would be effectiveness of GC. Isn't it the case? – Haris Hasan Jul 23 '11 at 10:46
  • No. You should only use **finalizers** to release unmanaged resources such as file and DB handles. However, the Dispose pattern is a completely managed thing put in place to release objects determinisitically. – Lucero Jul 23 '11 at 10:53
4

You are not disposing the timer after use. This is what is delaying its collection.

If your class contains objects which implement IDisposable (like the Timer does), it should also implement IDisposable.

public class TestTimer : IDisposable
{
    private Timer timer;
    public TestTimer()
    {
        timer = new Timer(1000);
        ...
    }

    #region IDisposable

    public void Dispose()
    {
        Dispose(true);
    }

    volatile bool disposed = false;
    protected virtual void Dispose(bool disposing)
    {
        if (disposing && !disposed)  
        {
            timer.Dispose();
            GC.SupressFinalize(this);
            disposed = true;
        }
    }

    ~TestTimer() { Dispose(false); }

    #endregion
}

Your main method should then look like this:

public static void Main()
{       
   using (TestTimer timer = new  TestTimer())
   {
       // do something
   }

   GC.Collect();
   GC.WaitForPendingFinalizers();
   Console.ReadKey();
}

Again, if your TestTimer is supposed to live longer than a scope of a single Main method, then the class which creates it and holds its reference should also implement IDisposable.

vgru
  • 49,838
  • 16
  • 120
  • 201
  • "This is what is delaying its collection" is not really correct, because what happens is that the `Timer` stays alive and it will keep the `TestTimer` alive through the reference in the event delegate. It would never get collected without disposal, no matter how long you wait. – Lucero Jul 23 '11 at 10:55
  • @Lucero: generally, any object with a finalizer will survive at least one collection, after which it will be moved to the "freachable" root list. Finalizers for objects in that list will be called from a separate finalizer thread. So, this will delay collection in a general case. But [your answer](http://stackoverflow.com/questions/6799818/why-cant-gc-figure-it-out/6799874#6799874) implies that timer does additional effort to secure it's not collected before it's disposed. I never realized this, as I haven't examined its source closely, and I always dispose them after use. – vgru Jul 23 '11 at 11:07
  • What is the reason behind calling `Dispose(false);` and even having the bool parameter? If it's false the method just returns without action. – Zebi Jul 23 '11 at 11:09
  • @Lucero: (and yes, `TestTimer` is then obviously alive because of the delegate, but I believe the OP was aware that `Timer` is the one which might be kept rooted) – vgru Jul 23 '11 at 11:10
  • 1
    @Zebi: it's just a general template for implementing IDisposable (check [this link](http://msdn.microsoft.com/en-us/library/ms244737%28v=vs.80%29.aspx)). If there are **unmanaged** resources referenced by a class, then calling `Dispose(false)` should only free them (not managed). If `Dispose(true)` is called, both managed and unmanaged resources need to be disposed. But if there are no unmanaged resources, it is correct to leave out the Finalizer (`~TestTimer()`) completely, as you noticed. – vgru Jul 23 '11 at 11:12
4

Why do you expect that an active timer can get collected in the first place? My expectation is that it acts as a GC root. Else the timer would stop working just because you don't have a reference anymore.

CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
  • isn't this expectation correct that if timer is not referenced or connected with any other thing and it is not in scope it should be collected? – Haris Hasan Jul 23 '11 at 10:50
  • 1
    It is connected to something. Probably rooted from some kind of static variable. – CodesInChaos Jul 23 '11 at 10:51
  • @HarisHasan: Even if nothing in the system holds a reference to a timer, it would be entirely possible (and not uncommon) for a timer-tick event to examine and/or make changes to objects which are also referenced elsewhere, and then for it to dispose of itself once it had done all that was going to need doing. In some cases, it's convenient to use a "fire and forget" paradigm, where a timer is created as a means of setting in motion a chain of behaviors; the code which creates the timer won't use a reference once it's been set in motion... – supercat Jan 04 '12 at 21:33
  • @HarisHasan: ... and the object the timer is going to act upon may not even be aware of the timer's existence. Such approaches need to be used with some care, to ensure that timers used for such purposes will in fact manage to dispose of themselves, but they can nonetheless be very useful. – supercat Jan 04 '12 at 21:35
2

When you start a timer timer.Start() a new thread(s) will start at the background, When you calling timer = null; you are not stopping the thread(s) that the timer used. The garbage collector will not kill or abort threads that are running no matter of the original object that create those threads.

Jalal Said
  • 15,906
  • 7
  • 45
  • 68
  • so is this only happening in case of `Timer` class? If I would have handled some other event output would have been same or not? or this thread model is same for all event handlers? – Haris Hasan Jul 23 '11 at 10:44
  • Timers don't have their own thread, that would be very wasteful. Instead, they use notifications and threads from a thread pool (in the case of `System.Threading.Timer`). – Lucero Jul 23 '11 at 10:46
  • @Jalal Do you have any sources for this? I wouldn't expect the CLR to create one thread per timer. – CodesInChaos Jul 23 '11 at 10:47
  • @CodeInChaos, ask the code or try it out... look at the Rotor code, fire up Reflector, dotPeek, ilSpy or any other decompiler and lookt at the source. A thread is an expensive resource, they shoulodn't be "wasted" for idle waiting. Note that `System.Timers.Timer` uses a `System.Threadin.Timer` internally. – Lucero Jul 23 '11 at 10:49
  • @Lucero that was directed to Jalal. My expectation is similar to yours. – CodesInChaos Jul 23 '11 at 10:50
  • @CodeInChaos: I didn't say **one thread by timer**, I just mentioned out that a new thread will started. I was not meaning by that it will be the only thread. I mentioned out the thread just to show the idea for him.. – Jalal Said Jul 23 '11 at 10:58
  • @Haris: the GC will not terminate any running thread even if the object that create that thread is go out of scope, for instance check [this](http://stackoverflow.com/questions/6407420/thread-creation-scope-and-the-garbage-collector) – Jalal Said Jul 23 '11 at 11:08
1

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.

See this thread for more details.

Community
  • 1
  • 1
CharithJ
  • 46,289
  • 20
  • 116
  • 131