From: Daniel Lidstrom on
"Andrew Baker" <ajb_news(a)anmel.co.uk> wrote in message
news:4c231fe8$0$12167$fa0fcedb(a)news.zen.co.uk...
> Because the timer is active (enabled), there's still a live reference to
> it, preventing the garbage collection. You've not got direct access to
> this reference, 'the system' has got one inside the workings of the .net
> framework somewhere.

Ah yes, that would explain it. Thanks for the insight Andrew.

Daniel

From: Adam Clauss on
On 6/24/2010 4:05 AM, Andrew Baker wrote:
> Because the timer is active (enabled), there's still a live reference
> to it, preventing the garbage collection. You've not got direct access
> to this reference, 'the system' has got one inside the workings of the
> .net framework somewhere.

I'm not sure that's accurate - I'm pretty sure I've seen cases where I
created and enabled a timer, forget to stash away a reference to it, and
the timer would fire once or twice - and then nothing. I assumed it had
gotten garbage collected.

-Adam
From: Peter Duniho on
Adam Clauss wrote:
> On 6/24/2010 4:05 AM, Andrew Baker wrote:
>> Because the timer is active (enabled), there's still a live reference
>> to it, preventing the garbage collection. You've not got direct access
>> to this reference, 'the system' has got one inside the workings of the
>> .net framework somewhere.
>
> I'm not sure that's accurate - I'm pretty sure I've seen cases where I
> created and enabled a timer, forget to stash away a reference to it, and
> the timer would fire once or twice - and then nothing. I assumed it had
> gotten garbage collected.

It depends on which Timer class you are using. There are three primary
Timer classes found in .NET. The System.Threading.Timer and
System.Timers.Timer classes require that the client code retain a
reference, otherwise the object will be GC'ed. But my recollection is
that System.Windows.Forms.Timer should continue to work even without an
explicit reference, for the reason Andrew mentions.

So, then the question becomes why the object isn't collected in Daniel's
sample code, since he's using one of the Timer classes that is subject
to GC. The answer to that is most likely because he's running a "Debug"
version of the program.

The secondary code block within his Main() method doesn't affect object
lifetime. References are managed at the method level. But the
optimization of releasing variables within a method after their last use
happens only in "Release" builds (i.e. optimized builds).

I don't have time to test it right now, but I suspect that the original
code will do what Daniel expects if it's compiled with optimizations
(i.e. as "Release").

Pete
From: Peter Duniho on
Mr. Arnold wrote:
> Daniel Lidstrom wrote:
>> "Mr. Arnold" <Arnold(a)Arnold.com> wrote in message
>> news:enGFmQ3ELHA.1316(a)TK2MSFTNGP02.phx.gbl...
>>>
>>> I guess you can just set the Timer to null Timer=null that should
>>> kill it.
>>
>> Neither setting timer nor timerTest to null helped in this case. Even
>> if it had helped I think that in a larger
>> application it becomes impractical to have to set unused references to
>> null.
>
> I guess you have never worked in the COM, DCOM or COM+ environment, back
> in the day for large applications, where objects had to be set manually
> to null to release resources. These technologies are still being used to
> this day.

That makes no sense. You don't set objects to null, you set variables
to null. And setting variables to null only has an effect on object
lifetime in GC'ed environments like .NET. In unmanaged code, setting a
variable that references a COM object to null has no effect on the
lifetime of that object whatsoever.

In fact, a major problem when writing COM code is failing to explicitly
call the Release() method exactly the right number of times to offset
each "reference count" that was added to the object, to ensure that the
object is in fact released and the memory freed. Getting those
reference counts and calls to Release() balanced is a major pain in the
neck and is why a GC system like what .NET uses is such a boon:
specifically because in .NET, setting a variable reference to null _is_
sufficient to release the object (eventually).

Using COM, it's entirely possible to have an object that is no longer
reachable � that is, no variable references the object directly or
indirectly � and yet which is still allocated. Being unreachable, there
would be no way to _ever_ free the object. In .NET, by definition an
object that is unreachable can and will eventually be freed.

Pete
From: David Boucherie & Co on
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. :P 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
}
}





Daniel Lidstrom wrote:
> Hello,
>
> I am trying to understand how the garbage collector works. To do this I
> created a simple test with a timer. I create a timer and then release
> all references leading to this timer, then I force a garbage collection.
> To my surprise the timer is still sparking off events! Here's the code:
>
>
> using System.Timers;
> using System;
>
> public class TimerTest
> {
> public TimerTest()
> {
> var timer = new Timer(1000);
> timer.Elapsed += (o, e) => { Console.WriteLine("Timer elapsed"); };
> timer.Start();
> }
>
> public static void Main()
> {
> {
> var timerTest = new TimerTest();
> }
>
> Console.ReadLine();
> GC.Collect();
> GC.WaitForPendingFinalizers();
> GC.Collect(); // necessary? does not seem to help
> Console.ReadLine();
> }
> }
>
>
> Can someone please explain why the Elapsed event is still firing after
> the first ReadLine()? Thanks in advance!
>
> Regards,
>
> Daniel Lidstr�m
>
>