RE: JavaMemoryModel: A problematical case for finalizers

From: Jerry Schwarz (jerry.schwarz@oracle.com)
Date: Thu May 22 2003 - 14:07:17 EDT


At 09:51 AM 5/22/2003, Boehm, Hans wrote:

With regards to (A) my intention is that the only required memory barriers
would be those related to garbage collection. I assume that the garbage
collector will ensure that all threads (except perhaps the one it is
running in if it runs in an ordinary thread) have done release stores
before it puts an object on the finalizer list. Otherwise I don't see how
it can be sure that the object isn't reachable. My reasoning with regards
to ordinary operations on the fields is that because the system knows that
the hidden volatile isn't going to be read until the finalizer runs it can
"defer" all the writes and their implied memory barriers until the next
synchronization or operation on a volatile. In other words the system
should never have to insert extra memory barriers in order to conform to
the semantics.

With regards to (B) I am using the phrase "naive semantics" in the way a
compiler writer does. And I believe in a way that I believe is consistent
with the way it is used in the JLS's discussion of reachability. When
considering a transformation (which might reorder reads and writes to
memory) the compiler writer asks whether the transformation affects the
"naive semantics". If it does then the transformation isn't allowed. The
"naive semantics" of a Java program is only the calls to natives (in
particular I/O operations) it does not include the reads and writes of
fields. The presence of threading, synchronization and volatiles sometimes
makes it hard to determine exactly what the allowed calls to natives are,
but reachability doesn't involve multi-threading and so the definition
doesn't need to deal with thread issues. Note that reachability only
affects the the timing of the calls to finalizers and not their semantics
which does involve threads.

As an aside, I've long wondered why the only operations I consider
significant, namely the calls to natives, are never mentioned in these
discussions.

>Jerry -
>
>I have some problems with both of these. Specifically:
>
>A. I think this doesn't interact well with inheritance. If you let the
>programmer do the synchronization, it's his/her responsibility to decide
>what the interesting last operations on the object could be. If you
>require that the implementation does it automatically, I expect you end up
>with those release stores all over the place. For example, presumably the
>default implementation of Object.hashcode() and Object.clone() needs the
>release store, because some class that inherits from it will have a
>finalizer, and it logically accesses a field? You might be able to have
>two versions of these operations, and implicitly override the simpler one
>when you add a finalizer, but this starts to sound nontrivial, both in the
>implementation and the spec.
>
>You really want to apply the volatile store treatment only to methods that
>can access the resource requiring finalization, and those are probably not
>in the ancestor classes. But I'm not sure the compiler has quite enough
>information to identify the right methods. And identifying them in the
>spec sounds messy.
>
>What if a method accesses multiple instances of a class with a finalizer,
>with some passed as a parameter? What if you're using a finalizer
>guardian and the actual handle needing cleanup is in a parent class?
>
>I think that getting this right is likely to lead to a complex spec with
>optimization impacts that we don't understand.
>
>B. We do explicitly allow optimizers to violate the naive semantics of
>Java by reordering ordinary reads and writes, and by not inserting memory
>barriers between all memory operations. (This relaxation probably results
>in a factor of 10-100 performance improvement on many architectures, so
>it's probably a good thing.) The JLS currently doesn't specify "naive"
>reachability. Given that memory operations can be reordered around the
>end of a block, I think it would be hard to do so. Thus I don't know how
>to make this precise.
>
>Hans
>
> > -----Original Message-----
> > From: Jerry Schwarz [mailto:jerry.schwarz@oracle.com]
> > Sent: Tuesday, May 20, 2003 10:48 AM
> > To: Boehm, Hans; 'Martin Trotter'; javaMemoryModel@cs.umd.edu
> > Subject: RE: JavaMemoryModel: A problematical case for finalizers
> >
> >
> >
> > I propose the following two changes
> >
> > A. Bill Pugh's suggestion that a class with a finalizer has
> > an implicit
> > volatile field that is written by all operations that access
> > the fields of
> > the instances of that class and read by the finalizer.
> >
> > B. Remove the permission that allows "optimizers" to violate
> > the naive
> > semantics of Java. We don't do that for any other semantics,
> > we shouldn't
> > do it for finalizers.
> >
> > I don't think anyone would object to these on functional
> > grounds. The only
> > objections I can imagine are on performance grounds. There would
> > potentially be some performance penalty for these, but it is hard to
> > predict how much and it is hard to compare it to the
> > overheads implied by
> > all the synchronize's that are needed in the absence of these changes.
> >
> > If implemented naively the penalty from A is comparable to that of
> > synchronizing everything as Hans advocates. However, because
> > the pattern of
> > use of this volatile is very specific the system might be
> > able to optimize
> > more effectively than it could do if just presented with the raw
> > synchronization. For example, it can take advantage of any
> > memory barriers
> > in it's gc.
> >
> > The penalty from B is harder to measure, but note that
> > usually it requires
> > no extra operations. Its cost is mostly in register usage. In
> > the most
> > common situations, where access to fields is only via methods
> > of the class,
> > the cost on many systems would be zero.
> >
> > This doesn't eliminate the problems associated with Hans'
> > reason (3), but
> > it does make dealing with them much simpler. In a typical case where
> > finalize is something like
> >
> > void finalize() { close(); /* release os resources */ }
> >
> > The issues associated with finalize will be exactly the same
> > as those of
> > multi-threading in general and no special care would need to be taken
> > beyond whatever already needs to be taken to deal with one
> > thread calling
> > close() while another thread is performing some other operation.
> >
> >
> > At 11:15 AM 5/19/2003, Boehm, Hans wrote:
> > >There are really at least three reasons to synchronize finalizers and
> > >other externally visible methods in that class:
> > >
> > >1) To prevent premature execution of the finalizer before the last
> > >method call. I believe that for most current implementations that
> > >do any sort of optimization, this is necessary. I started
> > this discussion
> > >by proposing to make it sufficient. With the current spec it isn't
> > >sufficient.
> > >I believe it is sufficient for all common JVMs.
> > >
> > >2) To ensure visibility too the finalizer of memory accesses
> > that occurred
> > >while the object was live. As Martin observed, current JVMs
> > generally
> > >ensure that anyway, even without explicit synchronization, except for
> > >compiler reordering issues. In spite of what Bill and
> > Jeremy's old proposal
> > >said, and I originally agreed to, I think it's clear now
> > that ensuring
> > >visibility for accesses to the object itself is
> > insufficient. Finalizers
> > >generally aren't used unless some of the interesting state
> > is outside the
> > >object.
> > >
> > >3) Even for a completely naive implementation on a
> > sequentially consistent
> > >machine (or a uniprocessor) a finalizer may still be run
> > concurrently with
> > >another method on the object, though this typically
> > indicates a bug or
> > >a malicious client. If you define a class A with a
> > finalizer and a method
> > >foo()
> > >which is unsafe to run concurrently with the finalizer, I can define
> > >class B with a field that has the only reference to an A
> > object a, and
> > >a finalizer that repeatedly invokes a.foo() in a loop. This
> > makes it quite
> > >likely (possibly after some tweaks to allow for JVM
> > idiosyncrasies) that
> > >a.foo() and a.finalize() will run concurrently.
> > >
> > >In all cases, if you fail to synchronize, the failure probability is
> > >small, at least in the absence of malicious code. But it is nonzero.
> > >
> > >I would be very interested in a proposal that
> > >
> > >a) Makes finalization simpler, and more exiting code correct, and
> > >
> > >b) Interacts reasonably cleanly with the rest of the memory
> > model, and
> > >
> > >b) Doesn't have a dramatic negative impact on optimization of classes
> > >without finalizers.
> > >
> > >But I think the most important steps in fixing this are:
> > >
> > >1) Agree on a proposal that makes it possible to write correct code
> > >with finalizers.
> > >
> > >2) Convince authors to fix Java textbooks.
> > >
> > >Some of the finalizer-related discussion in current
> > textbooks is so far
> > >off track that I would expect a lot of existing code that
> > uses finalizers
> > >to be in need of repair no matter what we do. (E.g. at
> > least one recommends
> > >that finalizers be used to clear circular references to make
> > life easier
> > >for the garbage collector. I haven't seen any textbook discussion
> > >of finalizers that I think would be adequate to teach correct usage.
> > >And without (1), that's certainly understandable.
> > >The slides from my POPL talk, which basically lists
> > finalizer myths, are at
> > >http://www.hpl.hp.com/personal/Hans_Boehm/popl03/slides.pdf )
> > >
> > >Hans
> > >
> > >
> > > > -----Original Message-----
> > > > From: Martin Trotter [mailto:martin_trotter@uk.ibm.com]
> > > > Sent: Monday, May 19, 2003 5:49 AM
> > > > To: javaMemoryModel@cs.umd.edu
> > > > Subject: RE: JavaMemoryModel: A problematical case for finalizers
> > > >
> > > >
> > > >
> > > > I quite agree that the finalization model needs to be as
> > intuitive as
> > > > possible and don't believe that in most cases programmers
> > > > should need to
> > > > put explicit synchronisation into the finalize method to get
> > > > the behaviour
> > > > they expect; that the results of operations done in the
> > > > methods of a class
> > > > are available to the finalize method of that class. I
> > > > previously appended
> > > > the following to the discussion but nobody has commented
> > further. I
> > > > believe that most if not all existing JVMs do the right thing
> > > > and that we
> > > > should at least specify as much in JSR133.
> > > >
> > > > ------- previous append ------------
> > > > I have been re-reading the discussion on finalizers. The
> > > > general trend of
> > > > the discussion is that all finalizers should be synchronized
> > > > since they can
> > > > be run on a separate thread. I don't believe that for most
> > > > usages and most
> > > > (if not all VMs) that rule is actually correct. The process
> > > > of identifying
> > > > objects to be finalized actually has the effect of ensuring
> > > > that the GC
> > > > thread (whichever that is) is guaranteed to have seen the
> > > > last write to the
> > > > object at the point that it realises that there are no
> > > > remaining strong
> > > > references to the object. So, does the GC thread pass on
> > > > that guarantee to
> > > > the finalizer thread ? I believe that in current VMs it does
> > > > since the
> > > > process involves a monitor controlling a list of
> > > > to-be-finalized objects.
> > > > If the VM chose to employ some atomic operation not
> > involving memory
> > > > barriers then of course the guarantee would not be passed on.
> > > > However, in
> > > > general a single monitor operation will perform much
> > better than many
> > > > atomic operations and thus I believe that current VM's
> > would have no
> > > > problem in making the guarantee that a finalizer will behave
> > > > as if there
> > > > existed a 'finalization lock' on the object. This lock is
> > > > released after
> > > > the last update of the object and obtained by the finalize
> > > > method. For a
> > > > large class of users this guarantee would be all they needed
> > > > to avoid them
> > > > worrying about synchronization simply as a consequence of the
> > > > VMs decision
> > > > to employ a separate finalizer thread.
> > > > --- end of previous append ---------
> > > >
> > > > I think I'm right in saying that the proposal is to add the
> > > > guarantees that
> > > > you are looking for to Thread.join etc. That's already a
> > > > part of JSR 133.
> > > >
> > > > Martin Trotter
> > > >
> > > > mtrotter@uk.ibm.com
> > > >
> > > > -------------------------------
> > > > JavaMemoryModel mailing list -
> > >http://www.cs.umd.edu/~pugh/java/memoryModel
> > >-------------------------------
> > >JavaMemoryModel mailing list -
>http://www.cs.umd.edu/~pugh/java/memoryModel

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



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