From: David Boucherie & Co on


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.



That doesn't seem to be it.

I tried Daniel's code with an int instead of a Timer, and still no joy.
I can't explain it...

See my answer before, about the workings of the garbage collector... I
thought I understood the thing pretty well. It seems I was delusional... :P



using System;

namespace ConsoleApplication1
{
public class SomeClass : IDisposable
{
int someHandle;

public SomeClass()
{
someHandle = 1;
}

~SomeClass()
{
Console.WriteLine( "Finalizing!" );
}

public static void Main()
{
{
SomeClass someObject = new SomeClass();
someObject.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 the handle." );
someHandle = 0;
}

#endregion
}
}
From: David Boucherie & Co on
Addendum:

It may be a good idea when implementing Dispose() to finalize your
object yourself and tell the garbage collector that there is no need for
it to finalize your object.

I didn't do that in my previous code, because I wanted to see when the
finalization fired.

In the code I sent before, write your finalization code in your
Dispose() method and leave the ~TimerTest() out.

public void Dispose()
{
Console.WriteLine( "Disposing timer." );
timer.Dispose();
GC.SuppressFinalize( this );
}

Since you have to Dispose() anyway, you can as well finalize yourself.
This gives as added bonus that you have control over when "finalization"
takes place.
From: Peter Duniho on
David Boucherie & Co wrote:
> Addendum:
>
> It may be a good idea when implementing Dispose() to finalize your
> object yourself and tell the garbage collector that there is no need for
> it to finalize your object.

Technically "finalize" refers _only_ to the execution of a finalizer.
One generally only ever lets the CLR call the finalizer. You don't call
it yourself.

> I didn't do that in my previous code, because I wanted to see when the
> finalization fired.
>
> In the code I sent before, write your finalization code in your
> Dispose() method and leave the ~TimerTest() out.
>
> public void Dispose()
> {
> Console.WriteLine( "Disposing timer." );
> timer.Dispose();
> GC.SuppressFinalize( this );
> }
>
> Since you have to Dispose() anyway, you can as well finalize yourself.
> This gives as added bonus that you have control over when "finalization"
> takes place.

The proper convention looks something like this:

class A : IDisposable
{
private IDisposable _managedDisposable;
private IntPtr _unmanagedResource;

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

protected virtual void Dispose(bool disposing)
{
SomeFunctionThatFreesTheIntPtrResource(_unmanagedResource);

if (disposing)
{
_managedDisposable.Dispose();
GC.SuppressFinalize(this);
}
}

~A()
{
Dispose(false);
}
}

Note that when the virtual Dispose(bool) method is being called by the
finalizer, the managed IDisposable object is ignored. This is because
generally, if the containing object is being finalized, then the
disposable objects it contains are also going to be finalized (assuming
no one else has any reference to them�which they shouldn't if there's
any chance your object is going to dispose it!). There's no point in
your own Dispose(bool) method trying to dispose the object; you've got
50/50 odds it's already been disposed anyway by the time your finalizer
runs, and it will be for sure if it can be, even without your own object
disposing it.

Pete
From: Peter Duniho on
David Boucherie & Co wrote:
> [...]
> Allocated objects (in the managed heap) get a generation:
>
> � 0: very-short lived objects (like temporary variables).

Careful with the terminology. A variable in and of itself isn't an
object, nor subject to collection in and of itself (though of course it
could be part of a heap-bound object).

Perhaps you mean "like objects referred to by temporary variables"?
Except that just because an object is referred to by a temporary
variable (whatever one might mean by that), that doesn't mean it's not
reachable by other means. So maybe instead you simply mean "like
temporary objects". But I would say that by definition, any "very-short
lived object" is a "temporary".

So I guess I don't really know what was intended in the parenthetical
statement.

> [...]
> 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!

That last statement is either incorrect, or you are being sloppy with
your terminology. Specifically: you describe objects in the
finalization queue as "dead objects that have finalizers", but then
describe other objects they reference as "alive".

Except that any objects reachable _only_ through an object that is about
to be finalized (i.e. already marked as "dead" and in the finalization
queue) have themselves already been marked as "dead" as well and if they
have finalizers, will themselves be in the finalization queue.

That's why it's so important to not attempt to use
disposable/finalizable objects from your own finalizer. They may have
actually been finalized before your own object.

Now, in reality, I don't think the word "dead" is really accurate here.
After all, an object isn't truly dead until the memory it was using
has been reclaimed and is no longer available as that object. And as
you point out, an object with a finalizer doesn't have its memory
reclaimed until it's been finalized. So there's not really any such
thing as a "dead object that has a finalizer". A truly dead object
doesn't have anything at all, no memory, no finalizer, no nothing.

> [...]
> 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 );

The parameterless overload of GC.Collect() is the same as calling
GC.Collect(2, GCCollectionMode.Force).

As for forcing collection and finalization, the code original posted
does exactly that. It calls GC.Collect(), which forces an immediate
collection of all generations, and then it calls
GC.WaitForPendingFinalizers(), which does not return until all the
objects in the finalization queue have been finalized.

> 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!

Did you try compiling the code as an optimized (i.e. "Release") build?
As I mentioned in a previous post, the JIT compiler is not as aggressive
in unoptimized builds, and local variables hold their references until
the very end of the method. Only in optimized builds is it possible
that the JIT compiler will shorten the lifetime of an object's reference
within a method.

> 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.

Disposable objects are supposed to have finalizers, and the
System.Timers.Timer object does in fact meet this requirement. As such,
it will eventually free whatever resources would normally have been
freed when the Dispose() method is called.

Besides, it doesn't even make sense to say that an object "will
eventually be collected" but then also say that "it will keep its system
resources [al]located". An object that has been collected can't
possibly _keep_ anything allocated.

If it's been implmented poorly, there might be a bug in which it fails
to free some resources, but that's highly unlikely in this case, given
the presence of a finalizer.

> I changed your code a bit, and to my astonishment, the Timer isn't
> collected...

There's nothing in the version you posted that would make the object any
more likely to be collected. Eligibility for collection has _nothing_
to do with whether an object has been disposed of or not, nor whether
GC.SuppressFinalize() has been called. It's entirely about whether the
object is reachable or not.

In your version, all you have done is wrap the reference to the Timer in
another object. But just as the Timer reference remained reachable in
the original code, so too does the reference to your own object, and
thus so too does the Timer reference contained within.

So for the same reason that the Timer isn't collected, your own object
is also not collected and so the Timer again remains uncollected.

Pete
From: Peter Duniho on
Daniel Lidstrom wrote:
> [...]
> Can someone please explain why the Elapsed event is still firing after
> the first ReadLine()? Thanks in advance!

I finally had some time (well, if I stay up late :) ) to try your code
example. In spite of being able to force other objects to be collected
(e.g. the TimerTest instance that is created), the Timer object is not
collected.

The only logical explanation is that the documentation is out-of-date
(or was never correct) and the Timer class implementation preserves a
reference to the object internally to .NET somewhere, even if your own
program does not, thus preventing it from being collected.

See below for a variation on the original code example that shows that
the TimerTest object is collected. It is implausible that it would be
collected but the Timer object would not be, unless the Timer object
really does still remain reachable via some other means (e.g. an
internal data structure in .NET).

Reminder: it is very important when running a test like this to run an
optimized build, and to _not_ run the program using the debugger (i.e.
just open a command prompt window and run it from there). An
unoptimized build or execution from the debugger will inhibit the GC/JIT
compiler behavior that we are trying to see.

Pete


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();
WeakReference weakLocal = new WeakReference(timerTest);

Console.WriteLine("press Enter to collect...");
Console.ReadLine();
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("collected; weakLocal is alive: " +
weakLocal.IsAlive);
Console.ReadLine();
}
}