RE: JavaMemoryModel: Proposal on finalizers

From: Boehm, Hans (hans.boehm@hp.com)
Date: Wed Apr 21 2004 - 18:20:50 EDT


> -----Original Message-----
> From: Bill Pugh [mailto:pugh@cs.umd.edu]
>
> Correct. Because the compiler is allowed to reorder uses, this has
> pretty minimal impact. The only real way to ensure something is kept
> alive while you are updating things referenced from the object
> is by using synchronization or a volatile write.

In that case, I'm OK with it. But the spec needs to be clear that's
what we mean. I.e. reachability is only guaranteed up to the earliest
point at which the read could appear to occur. I think this is nontrivial
to formalize, and hence hard to specify unambiguously.
> >
> >> So there are two recommended ways of writing finalizers:
> >>
> >> * The finalizer only depends on seeing writes performed
> >> in the constructor of that object
> >>
> >> * The finalizer using synchronization to ensure that
> >> it is correctly synchronized with all the writes it
> >> needs to see
> >>
> > You need to consider the underlying global data structure which
> > is cleaned up by the finalizer. Even if the object contains only
> > final fields, you may need synchronization (or keepLive()) on the
> > object to ensure that the cleanup does not happen early.
> >
> > The first option basically doesn't exist.
>
> I'm not sure I follow.
>
> Any writes performed during construction, even if they are to objects
> other than
> the object being finalized, will be visible to the finalizer.
>
> And if you are accessing a global, shared structure, you better be
> using synchronization.
>
The canonical last access to a finalizable object looks like

l1: h = <mostly read-only or final field of object>
l2: <do something to the entry of global data structure G referenced by handle h>

The canonical finalizer removes the part of G that is referenced by h.

Both the finalizer and l2 are likely to acquire a lock on G.

It's critical that the finalizer not run until l2 completes.

But there is not normally anything that
prevents the finalizer from running between l1
and l2, even if the access at l2 to G acquires a lock. The lock it acquires
will be on G, not on the finalizable object. And there are no non-finalizer
accesses to the finalizable object past l1. Hence the finalizer can run,
acquiring and releasing the lock on G, all before l2.

This may result in the finalizer not seeing the last update to the part of G
that corresponds to h. But it fails even if l2 only reads G; it will see
a data structure that has been clobbered by the finalizer.

Basically you need to lock the finalizable object, not just G, around l1 and l2,
or you need some sort of volatile write or keepLive() call after l2. And that's
true even for read-only objects.

Hans

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



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