RE: JavaMemoryModel: finalization

From: Boehm, Hans (hans_boehm@hp.com)
Date: Fri Apr 04 2003 - 19:00:37 EST


> From: Bill Pugh [mailto:pugh@cs.umd.edu]
> Sorry to have sat out on much of this discussion. I haven't read
> through every posting, so let me know if I missed out on some
> important point.
>
> My 2 cents:
>
> First, I think we clearly need to have the following requirements:
>
> * The finalizer sees all writes that are happen before the end
> of the constructor.
>
> * The finalizer sees all writes to fields of the object that
> occur before
> the finalizer is invoked.
I'm no longer convinced that those rules are particularly useful or necessary, since it's very hard to write correct code that takes advantage of them. If you synchronize finalizers and methods that may involve the last access, both of those guarantees are automatic. (The same applies with the volatile write technique you suggest at the end.)

I'm afraid that these effectively add rules that make it easier to write "almost correct" finalizers, but don't matter for correct ones. I'm sorry I didn't fully understand this earlier.

The first rule probably also has a compiler impact that I hadn't appreciated before: The compiler needs to make sure that the finalizer doesn't run before the constructor finishes, something that most of us don't believe is currently guaranteed. I suspect there are JVMs for which it's not true in some contrived cases. (I also suspect all current major JVMs already satisfy the rules I proposed.)
>
> I don't think we want or need any additional rules.
I think Jerry and I both claim you can't write correct code without some additional rule about reachability.
>
> At 12:07 PM -0800 3/20/03, Boehm, Hans wrote:
> >2) We should add a constraint that any object is reachable (for
> >finalization/GC purposes) when its lock is released. Since prior
> >memory references are ordered before the lock release, that should
> >ensure that visible operations on A[x_index] are complete before the
> >finalizer runs.
>
> If the finalizer is synchronized, you will get this guarantee
> automatically.
Why? I disagree.

The finalizer can run completely before the last method call on the object. Consider a class foo containing a handle (or file descriptor or index) in a final field "h", which must be explicitly closed by foo's finalizer.

Now assume that foo has a method

synchronized void bar()
{
  do_something(h);
}

The last action on a foo object x is

for (int i = 0; i < 1000000; ++i) {
  x.bar();
}

What prevents the JVM from inlining bar() and compiling this to:

my_h = x.h;
for (int i = 0; i < 1000000; ++i) {
  synchronized(x) {
        do_something(my_h);
  }
}

?

In that case, if synchronized(x) does not guarantee reachability (e.g. because it only accesses the entry for x in a hash table), x may become unreachable before the loop, and may be finalized at the beginning of any loop iteration, leading to failure. I see nothing in the JLS to prevent this. And I claim that finalization is useless unless you do prevent it.

You are correct that so long as the finalizer is synchronized, we only need to require that x be viewed as reachable someplace within synchronized(x) { ... } after we have acquired the lock. If you prefer that statement, I'm happy with it.
>
> And I don't see any problem with requiring that people use
> synchronization in their finalizers.
I agree that synchronizing the finalizers is a nonissue. Synchronizing methods that might contain the last access is more of a performance issue, but I don't think it's one we should address either. (If you're willing to ensure that your volatile write technique below is correct then that's one way of partially addressing it.)
>
> Since we want to allow compilers to be able to remove useless
> synchronization, we shouldn't enforce any other semantics (such as
> guaranteeing liveness) for useless synchronization.
I think the choice is between doing something along these lines or just declaring finalization (and java.lang.ref) broken. And the cost of ensuring reachability is small, at worst a register spill to the stack. (And with the proposal as I stated it, you probably don't need to do it in most cases.)
>
> At 12:07 PM -0800 3/20/03, Boehm, Hans wrote:
> >- If Object x refers to object y via a final or volatile field and x
> >is reachable, then y must be reachable.
>
> No. I think we want to allow the compiler to remove unused fields.
At the expense of breaking code that competent people (like Joshua) believe to be obviously correct? And at the expense of not being able to enforce finalizer ordering at all?

This proposal still allows removal of fields that are neither final nor volatile. (I could settle for just "volatile", but that would require require adding a "volatile" to the "finalizer guardian" idiom from Joshua's book and the like.)
>
> If you want to guarantee visibility for finalization, another
> alternative is to have a volatile field that is written at the end of
> every method, and that is read at the beginning of the finalizer.
>
> No operations before the write to the volatile field can be reordered
> with the volatile write, and the finalizer cannot be invoked until
> after the volatile write has been performed. And all of the writes in
> any of the methods will be visible to the finalizer.
You're assuming that a volatile write implies reachability. I also don't see why that's currently guaranteed. I think the obvious interpretation of 12.6.1 is that it isn't.

Consider the case in which the JVM can see the whole program, and the value from the volatile field never propagates into anything visible. Can I replace the per object fields by a statically allocated one to save space, while preserving the ordering semantics? My interpretation of the JLS is "yes". (I agree that it should be "no".)

Hans
>
> Bill
>



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