Discussions on Java memory model

Jeff Sturm jsturm@sigma6.com
Mon Jun 12 18:33:00 GMT 2000


(This is slightly off-topic for java-discuss, but includes information
that should be interesting to any VM implementor.)

There is ongoing discussion regarding the Java memory model, regarding
the requirements outlined in ch. 17 of the JLS, and existing
implementations.  Bill Pugh and Doug Lea presented their findings at
JavaOne.  Their are trying to gather steam for a formal JSR that will
amend/replace chapter 17 on the basis that:

o the requirements in the JLS are ambiguous and unclear

o no existing VM correctly implements the semantics required by the JLS

o common coding idioms rely on behavior not specified by the JLS.

Some of their findings are surprising.  For one, volatile variables are
seldom used in Java, and incompletely implemented in all existing
compilers (gcj included).

Consider two instance variables i, j:

  int i = 0;
  volatile int j = 0;

First thread A mutates i, j:

  i = 2;
  j = 1;

Next thread B tests j for initialization:

  if (j > 0) System.out.println(i);

The JLS says thread B must print nothing or "2"... it cannot print "0". 
To this end, loads/stores of volatiles must be sequentially consistent
wrt other variables.  In addition, loads/stores of volatile longs or
doubles are guaranteed to be atomic (variable j could be "long" above).

In terms of gcj, volatile doesn't appear to be implemented at all: I
haven't investigated the compiler much, but I've seen the same object
code generated (except for metadata) when I removed occurences of
volatile.

On certain machines, gcj must accompany volatile loads/stores with
memory barriers if it is to be compliant with the proposed spec.  On
most 64-bit machines, atomic loads of long/double are guaranteed; on
others (ia32) the compiler probably must wrap long/double volatile
accesses in a mutex (fortunately those should be rare).

Somewhat more disconcerting is that the contributors have found that
object initializers cannot easily be guaranteed to be safe.  Consider
the following:

  String foo;

  public String getFoo() {
    if (foo == null) foo = "foo" + 1;
    return foo;
  }

This is the "unchecked initialization" idiom.  It is widely believed
that this method will always return a fully initialized string, either
allocating a new string or not.  However due to weak memory models on
some multiprocessing architectures, it is possible for the above to
return an uninitialized string, with very bad consequences:

o the object header (including vtable) could be uninitialized, causing
  a core dump, and

o the string contents may appear to change, breaking immutability (and
  Java's security model along with it).

It appears likely that the initializer problem will be fixed by
tightening the semantics of "final" variables.  In the worst case (e.g.
SMP Alpha), the compiler could emit a memory barrier after
initialization and before reading a final variable.

Nothing may have to be done for SPARC users, due to its TSO (total store
ordering) policy on Solaris.  Very likely, Alpha and ia64 will require
special instructions to guarantee initializer safety.

More information and test cases are available on Bill Pugh's web site:

http://www.cs.umd.edu/~pugh/java/memoryModel/

(I doubt many are using unusual hardware with gcj yet... just thought
I'd pass along the information.)

--
Jeff Sturm
jsturm@sigma6.com


More information about the Java mailing list