Re: JavaMemoryModel: Finalizers

From: Joseph Bowbeer (jozart@csi.com)
Date: Sat Apr 05 2003 - 17:02:12 EST


Bill Pugh writes:

> OK, Josh sent me a description of his finalizer guardian,
> and I'm going to argue that we shouldn't guarantee that
> it works.

Just so everyone's on the same page,

The coding guideline I've encountered is: "in a nonfinal public class that
has a finalizer, use a finalizer guardian," where a finalizer guardian is an
inner class whose sole purpose is to finalize its enclosing instance.

Here's the example from Josh's book:

  class Foo {
    private final Object finalizerGuardian = new Object() {
      protected void finalize() throws Throwable {
        /* finalize outer Foo object */
      }
    }
  }

The finalizer guardian guards against a subclass overriding finalize and not
calling super.finalize.

> I think we need to allow for fields that will never be read to be
> considered dead. As an example, it is very typical for inner
> classes to not actually need references to the enclosing instance.
> In Sun's JDK 1.4.2 rt.jar, there are 367 inner classes that never
> dereference this$0. We want to allow the compiler to remove these
> fields, because they can keep the outer instance alive past any
> possible use.

The guardian's sole purpose is to finalize this$0 so I would assume that it
also dereferences this$0.

Wouldn't it be more straightforward to convert the unnecessary inner classes
in the JDK to static nested classes?

----- Original Message -----
From: "Bill Pugh" <pugh@cs.umd.edu>
To: <javamemorymodel@cs.umd.edu>
Sent: Saturday, April 05, 2003 10:13 AM
Subject: JavaMemoryModel: Finalizers

OK, Josh sent me a description of his finalizer guardian, and I'm
going to argue that we shouldn't guarantee that it works. I think we
need to allow for fields that will never be read to be considered
dead.

As an example, it is very typical for inner classes to not actually
need references to the enclosing instance. In Sun's JDK 1.4.2 rt.jar,
there are 367 inner classes that never dereference this$0. We want to
allow the compiler to remove these fields, because they can keep the
outer instance alive past any possible use.

Here are a couple of suggestions what additional features/functions
we can provide that will make it possible to write correct code for
finalizers.

* We provide a new static method in class java.lang.System:

public static void keepAlive(Object o)

   The semantics of the keepAlive method are as through it writes to
   a hidden volatile field of o. This hidden volatile field is read
   at the beginning of the finalizer for o. The call to keepAlive
   is an active use that prevent the object o from being garbage collected
   or finalized until after the call to keepAlive is complete.

   Perhaps the end of the outermost constructor for a class should be
considered an implicit call to System.keepAlive(this).

   Most of the issues we've discussed would be solved by putting a call to
keepAlive(this) at the end of any method that uses this.

   Of course, keepAlive doesn't actually have to do anything, other
than keep the reference alive and act as a memory barrier, so it will
be fairly cheap.

   keepAlive can also be used to guarantee visibility for weak
references. This means that we can't simply statically eliminate
keepAlive calls on classes that don't have finalizers.

* Synchronizing on an object does count as an active use of the object
   that keeps the object alive, but only if invoking finalization on
that object might result in synchronizing on that object.

* Many/most finalizers exist only to free data structures associated
   with an object that are allocated in the C heap. To help eliminate
   these finalizers, we provide a new function that can be called from
   native code, called something like:

void freeOnceGarbageCollected(Object ref, void * memory)

   this function has the semantics that once ref has been garbage collected,
   the C heap memory is freed

* Any object is considered alive for the duration of any JNI call such that
   a reference to the object has been passed as an argument to the JNI call.

* To handle the problem mentioned by Josh, (the fact that finalize methods
   can forget to invoke super.finalize()), we can provide a new static
method
   of System

public static void runOnceGarbageCollected(Object ref, Runnable task)

   This would use a reference queue so that once ref is GC'd, task would be
run.
   Of course, one problem here is that if the task object had a reference to
   the ref object, the ref object would never be GC'd.

   We could also (and this is a little ugly), allow a Meta-tag on a
finalize method to indicate that it should always be invoked, even if
overridden.

-------------------------------
JavaMemoryModel mailing list - http://www.cs.umd.edu/~pugh/java/memoryModel



This archive was generated by hypermail 2b29 : Thu Oct 13 2005 - 07:00:43 EDT