Say, I have this class:
class Test
{
readonly object _child = new Object();
// ...
~Test()
{
// access _child here
// ...
}
}
Is the _child
object guaranteed to still be alive when ~Test
is called by the garbage collector? Or should I first "pin" the _child
with GCHandle.Alloc
in the constructor?
The most adequate explanation so far IMHO can be found here (see Karlsen's answer), and so to summarize an answer to the OP:
Any reachable (child or external) object during finalization of an object will still be available in memory, but the state of such an object will depend on whether this object has already been finalized itself - as there is no specific order of finalization between objects in the finalization queue.
In short, quoting from the posting:
You can however, use any other reachable object (with no finalization) safely during an object's finalization stage, e.g. a string. In practical terms this means it is safe to use any reachable object that does NOT implement the IDisposable interface.
Being a
readonly
field_child
you can't lose its reference (unless you set it to null via reflection). Which means that till theTest
is garbage collected_child
will stay in memory for sure.Also, you're using a
Finalizer
which is called prior to garbage collection, Only on next pass the object's memory will be reclaimed, at this point_child
will be alive. In other words whenFinalize
method is called_child
will be alive and safe to access it.Finalizer gets called doesn't mean memory will be reclaimed, If you do something like the following
Finalize
will be called but memory will not be reclaimedFinalizers are almost never needed when you're dealing with managed code, It adds extra work to the garbage collector and also strange things can happen as we seen above.
Update: If you use
_child
only forlock
it is safe to use, because the_child
instance will not be null, which means it points to a valid reference.Monitor.Enter
andMonitor.Exit
just cares about the references it is absolutely safe to use it(only for locking).There is a workaround: You can inherit the
Child
class fromSafeHandle
and that does the trick. It will make sure if bothTest
andChild
goes out of scope at the same time, It will callTest's
finalizer first as theChild
inherits fromSafeHandle
which delays its finalization. But, IMO don't depend on this. Because other programmers work with you may not be knowing this which leads to misconception.Quote from SafeHandle: A Reliability Case Study
I created a simple test:
The output (Release build):
Thus, the
_child
gets finalized first. So, I need to doGCHandle.Alloc(_child)
in the constructor, and doGCHandle.Free
in the finalizer, after I'm done with_child
.To answer the comment why I need all of this:
_child
is referenced in a method which is called from the finalizer to access an unmanaged resource. That method doeslock (_child) { ... }
and it can be called from outside the finalizer, too.