[gclist] Finalization and death notices

Nick Barnes Nick.Barnes@pobox.com
Tue, 09 Oct 2001 17:13:00 +0100

At 2001-10-08 16:16:42+0000, "Kerns, Bob" writes:

> The drawback to all this, compared to death notices, is that
> presence on the queue extends the object's lifetime.
> A buggy mutator that forgot to close the file stream not only leaves
> open the file descriptor -- it also leaves allocated the buffer
> storage, index B-Tree, pool of connection objects, and other things
> associated with the file.

MPS users have not found this to be a problem.  If you consume a
critical resource whose replenishment depends on finalization, you
need to have correct finalization no matter what the mechanism.
Unsurprisingly, bugs which limit the availability of critical
resources are both serious and (usually) quite easy to find with
stress testing.

If there are too many other resources also retained, you should
probably select a different object for finalization (e.g. something
closer to the file descriptor in your example).

And, of course, using finalization to release really limited and
critical resources (such as file descriptors in some applications) is
often a mistake, because finalization is neither prompt nor sure (in
most garbage collectors, including the MPS).  You should only use
finalization if your design allows no other option.  Even in that
case, you should consider changing your design rather than let the
reliability of your whole system depend on the exactness and
promptness of your GC.  For instance, you could abstract file
descriptors away entirely and use a stream abstraction which can be
multiplexed on whatever file descriptors are actually available.  Then
using finalizers for the descriptors themselves is a more reasonable
design decision because some delays in finalizing the descriptors will
not lead to system failure.

A more common example in our experience is finalizing non-kernel
objects managed by a third-party legacy library.  In this case, the
external resource freed by the finalizer is memory.  We are already
relying on the GC to free memory surely enough and promptly enough, so
using finalizers here does not introduce a new risk.

> I know, I padded this list beyond a bare buffered file stream; the
> point is, this is an unbounded amount of storage to hang onto, just
> to enable a final cleanup on the file descriptor!
> I like true death noticies, instead of these "near death notices",
> because they focus on the real requirements.

The whole MPS design and implementation is guided by requirements
analysis.  The relevant requirement at the time was supporting an
implementation of Dylan, which required finalization.  So we
implemented finalization.

It seems to me that finalization and death notices are trivially
equivalent in power (in the sense that either can be readily
implemented on top of the other).  If I'm wrong it's because I'm just
guessing the semantics of death notices.  Can you tell me the real
semantics of death notices?

Nick B

> -----Original Message-----
> From: Nick Barnes [mailto:Nick.Barnes@pobox.com]
> Sent: Monday, October 08, 2001 4:27 AM
> To: gclist@iecc.com
> Subject: Re: [gclist] Finalization and death notices 
> All this talk about finalization prompts me to describe the
> finalization system we have in our Memory Pool System (MPS).  Here it
> is:
>   The mutator may register an object for finalization.  It may
>   register the same object for finalization more than once (as if
>   incrementing a counter).
>   When the MPS observes that an object registered for finalization
>   would otherwise be reclaimable, it deregisters it for finalization
>   (as if decrementing the counter) and adds a finalization message,
>   containing a reference to the object, to its message queue.
>   The message queue may be read by the mutator, at the mutator's
>   convenience.  Messages must be explicitly destroyed by the mutator.
> Notes:
> (1) The object remains alive until (at least) such time as the mutator
> destroys the finalization message.  The mutator is free to do what it
> likes with the object, including create new references to it and
> re-registering it for finalization.
> (2) Finalizers (if any) get run by the mutator, synchronously with the
> mutator's other activity, at the mutator's convenience.  They are not
> run by the MPS.  Any problems with locking, threads, and object
> ownership are up to the mutator to resolve (as usual).  It is simple
> for the mutator to run finalizers by module (by adding another queue,
> with filtering by module, on the mutator side).
> (3) Finalization ends when a finalization message is enqueued.  As far
> as the MPS is concerned, any "finalizer" is just another part of the
> mutator.
> (4) Ensuring that finalizers are run eventually is a problem for the
> mutator.  If the mutator doesn't read the queue, or doesn't run the
> finalizers, or takes an early bath with messages still on the queue,
> that is the mutator's problem.
> We don't use liveness terminology in this context, because the object
> in question is certainly _not_ dead.  How can it be dead when I have a
> pointer to it right here?  Talk about liveness in the context of
> finalization leads to all sort of semantic nonsense like "resurrection
> of the dead".  The mutator has simply asked the GC to do it a favour
> by using the GC's uniquely global knowledge of object reachability to
> let it know when an object is not _otherwise_ reachable.
> Nick Barnes
> Ravenbrook Limited