GC.SuppressFinalize

  • Thread starter Thread starter Stephen
  • Start date Start date
S

Stephen

In Visual Studio's help for GC.SuppressFinalize, it gives the following
example:

public void Dispose()
{
Dispose(true);
// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SupressFinalize to
// take this object off the finalization queue
// and prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}

All other examples I've found on the web also include the
GC.SuppressFinalize method at the end of the Dispose method. I would have
thought that as soon as I know that the Dispose has been called (ie the
start of the method), I would want to remove the object from the
finalization queue. Is there a reason why GC.SuppressFinalize isn't the
first call in the Dispose method?
 
If IDisposable.Dispose is being invoked there is at least one reference to
the object. You can suppress finalization at any point in the method with
the same effect, since there's not going to be any collection or
finalization in the interim.
 
Are you sure? I was under the impression that 'this' didn't count as a
reference, hence the need for GC.KeepAlive.
 
If IDisposable.Dispose is being invoked there is at least one reference to
the object. You can suppress finalization at any point in the method with
the same effect, since there's not going to be any collection or
finalization in the interim.

Wanna bet? :)

using System;
using System.Threading;

public class Test : IDisposable
{
~Test()
{
Console.WriteLine ("Finalizer called");
Dispose();
}

public void Dispose()
{
Console.WriteLine ("Starting Dispose");
Thread.Sleep(500);
GC.Collect();
Thread.Sleep(500);
Console.WriteLine ("Ending Dispose");
}

static void Main()
{
Test t = new Test();
t.Dispose();
}
}

The Sleep calls are just to make it clearer. The GC.Collect() is to
make sure that garbage collection happens at the right time - often it
wouldn't, but this proves that it *can*.

See http://blogs.msdn.com/cbrumme/archive/2003/04/19/51365.aspx for
more details.
 
This is a reference, and it counts as a valid reference, but only if it's
being used to access instance data. If an instance method is executing,
there must be a root somewhere that can be traced to the instance via a
reference, and the object won't be collected (and later finalized) while the
method is executing assuming that referenced instance data is being
accessed, which is the whole point of Dispose(). You can break this
guarantee by extracting references into non-instance data, but that's a bad
habit to get into, and it's a fairly narrow edge case that doesn't really
get affected by when you call GC.SupressFinalize(). Given type Z, used by
method X.F() like this:

class Z
{
// ....
public void DoSomething() { ... }
}

class X{
public void F(Z z)
{
z.DoSomething();
// other work not involving z...
}
}

X.F is allowed to report z as an unused reference as soon as z.DoSomething()
is invoked, assuming that there is no further activity with z inside X.F
(X.F is not required to wait until z.DoSomething() completes). As a
practical matter though, the processor (or call stack) will then hold a
reference to the object referenced through z, and those are considered roots
to begin tracing. However, if Z.DoSomething() does not require access to
instance data, then the object could still be collected during
Z.DoSomething(), as the roots have no effect. Inside DoSomething, as soon as
the need for a reference to instance data is removed, then DoSomething() is
free to report no need for the reference. As a practial matter, this is only
an issue when you allow finalization-affected references to be exposed or
extracted independently of the objects that encapsulate them.

The need for GC.KeepAlive occurs because if you extract a field into another
reference, and start using that *new* reference instead of the instance's
field, the instance might no longer have any valid references reported
during a collection. If no finalizer exists, there is no effect. If a
finalizer exists, there can be a race condition between the finalizer
working on the instance-bound reference, and any methods that may be
executing using other reference aliases. So this would be an example of
Z.DoSomething() that requires GC.KeepAlive (assuming Z has a finalizer):

public void DoSomething() {

object obj = _myCriticalResourceThatIsFinalized;
// no longer need *this
SomeStaticClass.SomeStaticMethod(obj);
// need to call GC.KeepAlive here
}

Assuming Z has a finalizer that works on _myCriticalResourceThatIsFinalized,
and if GC.KeepAlive is not called where indicated, then
myCriticalResourceThatIsFinalized could get finalized while
SomeStaticClass.SomeStaticMethod is executing on the obj reference, which is
actually the same object.
 
I probably should have been more clear that the Dispose method must actually
do real work with instance data.

I get exactly the behavior I would expect on my system. Dispose is called,
and the finalizer is called because the Dispose method doesn't access any
instance fields. Change the Dispose method to look like a real-world
Dispose, and you'll see that it doesn't get finalized.

--
Mickey Williams
Author, "Microsoft Visual C# .NET Core Reference", MS Press
www.servergeek.com
 
I have been following this and aonther thread in a related newsgroup
with interest and have a further question (probably for Jon).

Does this mean that SuppressFinalise also has the semantics of KeepAlive
ie the object is ineligible for garbage collection from the start of the
Dispose routine?

Some clarification would be helpful. This is explicitly stated in MSDN
for KeepAlive.
In Visual Studio's help for GC.SuppressFinalize, it gives the following
example:

public void Dispose()
{
Dispose(true);
// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SupressFinalize to
// take this object off the finalization queue
// and prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}

All other examples I've found on the web also include the
GC.SuppressFinalize method at the end of the Dispose method. I would have
thought that as soon as I know that the Dispose has been called (ie the
start of the method), I would want to remove the object from the
finalization queue. Is there a reason why GC.SuppressFinalize isn't the
first call in the Dispose method?

--
If you wish to reply to me directly, my addres is spam proofed as:

pbromley at adi dot co dot nz

Or if you prefer - (e-mail address removed) :-)
 
I probably should have been more clear that the Dispose method must actually
do real work with instance data.

I get exactly the behavior I would expect on my system. Dispose is called,
and the finalizer is called because the Dispose method doesn't access any
instance fields. Change the Dispose method to look like a real-world
Dispose, and you'll see that it doesn't get finalized.

That depends on exactly how you structure things. For instance you
might do:

int myHandle = handle;
// Garbage collection could occur here!
FreeHandle (myHandle);


where FreeHandle is a static method, and handle is an instance
variable. As soon as the method no longer touches any instance
variables, it no longer needs the reference to "this" even though it's
still going to do something with a resource effectively held by this
instance.
 
Peter Bromley said:
I have been following this and aonther thread in a related newsgroup
with interest and have a further question (probably for Jon).

I'm far from an expert on this kind of thing - I've just read some
articles which have pointed out problems :)
Does this mean that SuppressFinalise also has the semantics of KeepAlive
ie the object is ineligible for garbage collection from the start of the
Dispose routine?

I'm not sure. GC.KeepAlive is basically a method which does nothing but
about which the GC *knows* nothing. I don't know whether the GC has any
specific knowledge of SuppressFinalize which would enable it to do
tricky things. I would hope not, but I don't know for sure.
 
Jon said:
I'm far from an expert on this kind of thing - I've just read some
articles which have pointed out problems :)




I'm not sure. GC.KeepAlive is basically a method which does nothing but
about which the GC *knows* nothing. I don't know whether the GC has any
specific knowledge of SuppressFinalize which would enable it to do
tricky things. I would hope not, but I don't know for sure.

The GC doesn't need any special knowledge in this case. If you have the
call

GC.SupressFinalize( this);

the reference to 'this' in the call would be enough to keep the object
alive, so it would have (as a side-effect) the effect of GC.KeepAlive()
- just like any other live reference would.
 
Jon said:
That depends on exactly how you structure things. For instance you
might do:

int myHandle = handle;
// Garbage collection could occur here!
FreeHandle (myHandle);


where FreeHandle is a static method, and handle is an instance
variable. As soon as the method no longer touches any instance
variables, it no longer needs the reference to "this" even though it's
still going to do something with a resource effectively held by this
instance.

While this (and Chris Brumme's blog entry) is certainly very
interesting, I'm not sure that it causes a real problem in most cases
(at least of implementing IDisposable).

For the example above, where garbage collection might occur before
FreeHandle() is called, it still remains that FreeHandle() will be
called with the intended handle.

Now, there could be a problem if there's improper synchronization
between the finalizer and the Dispose() method, but if you're calling
GC.SuppressFinalize() somewhere in the dispose handling, then the
synchronization automatically happens - since if you call
GC.SupressFinalize( this), the finalizer will never be called while
executing Dispose.

Before calling SupressFinalize(), the finalizer won't be called, since
there's still a live reference to the object, and after
SuppressFinalize() it won't be called because that's what
SupressFinalize() does.
 
Back
Top