This is a bit simplified, but in essence, the garbage collector works as
follows...
First off, the garbage collector runs whenever:
· Physical memory is low.
· The memory that is used by allocated objects (on the managed heap)
surpasses a certain threshold (which is dynamically calculated while
running).
· GC.Collect() is called (the method you use in your code).
Allocated objects (in the managed heap) get a generation:
· 0: very-short lived objects (like temporary variables).
· 1: short-lived objects
· 2: long-lived objects
Garbage collection is run on a specific generation depending on certain
conditions. All objects of that generation or lower are then collected.
(A generation 2 garbage collection is also known as a full garbage
collection.)
Objects that "survive" a collection pass are promoted to the next
generation. So when a generation 0 garbage collection occurs, any object
not collected becomes generation 1, 1 becomes 2, and 2 remains 2.
A full garbage collection has three phases:
· Live objects are marked, dead objects become "condemned".
· References to objects that will be compacted (moved together, simply
put) are updated.
· Space occupied by dead objects is reclaimed and surviving objects are
compacted. However, dead objects that have finalizers are not reclaimed,
but marked as finalize pending. Also, all references in the finalizers
cause the objects referenced to be kept alive too, even if they would
otherwise be condemned!
A finalization thread is started after collection is complete and starts
executing all pending finalizers, causing the finalized objects to
become dead again. However, they are not collected until the garbage
collector makes its next run.
On top of that, full garbage collection is expensive in time and CPU
power, so usually garbage collection is only partial and works only on
lower generation objects.
In short: when you run your garbage collector, there is no way to be
sure that a certain object will be effectively destroyed at any one
time, nor can you exactly predict when finalizers will be run.
To force a full garbage collection (generation 2), use:
GC.Collect(2, GCCollectionMode.Forced );
Now that I have explained all that... I have NO CLUE why your timer
doesn't get killed.
![Stick Out Tongue :P :P](/styles/default/custom/smilies/tongue.gif)
It doesn't even finalize after disposing it, even
when forcing a full garbage collection! The finalizer only runs when the
program ends (after ReadLine()).
I suppose some hidden references exist... but I don't see where. I even
tried with a simple class, replacing your timer with an int instead, and
still no joy.
Anyone can explain it? Because I can't!
On a side note:
Your code contains a resource leak.
Your Timer will eventually be collected, but as you don't Dispose() it,
it will keep its system resources located.
I changed your code a bit, and to my astonishment, the Timer isn't
collected...
using System;
using System.Timers;
namespace ConsoleApplication1
{
public class TimerTest : IDisposable
{
Timer timer;
public TimerTest()
{
timer = new Timer( 1000 );
timer.Elapsed += ( o, e ) => { Console.WriteLine( "Timer elapsed
" + e.SignalTime.ToLocalTime() ); };
timer.Start();
}
~TimerTest()
{
Console.WriteLine( "Finalizing!" );
}
public static void Main()
{
{
TimerTest timerTest = new TimerTest();
timerTest.Dispose();
}
Console.WriteLine( "Collection 1." );
GC.Collect( 2, GCCollectionMode.Forced );
Console.WriteLine( "Suspending thread waiting for finalizers." );
GC.WaitForPendingFinalizers();
Console.WriteLine( "Collection 2." );
GC.Collect( 2, GCCollectionMode.Forced );
Console.ReadLine();
}
#region IDisposable Members
public void Dispose()
{
Console.WriteLine( "Disposing timer." );
timer.Dispose();
}
#endregion
}
}