You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by Brian Goetz <br...@lx.quiotix.com> on 2001/01/28 23:18:57 UTC

Re: Memory Model discussion

> Care to comment?

OK. 

Compiler-based reordering is not the only source of reordering.  In
fact, the whole compiler-based reordering issue is kind of a red
herring -- its really just an example of one of many types of
reordering that occurs in the absense of synchronization, but is the one
which software people feel the most competent to reason about and
therefore make assumptions about.  Most reorderings come from hardware
-- processors, memory, caches.  The only way to manage those is
through, you guessed it, synchronization.

Supposing the the instructions are actually generated in the order
that you think they should be generated in.  (And ignore that it is
still possible for processors to execute them out of order, or execute
them in parallel.)  In the presence of multilevel caches, it is
possible, and quite common, for memory write A to be committed to main
memory before memory write B, even if B preceded A.  In the absense of
explicit or implicit memory barriers, this is perfectly legal and
anticipated by the JMM, and without allowing this, performance would
suffer badly.  (Those of you with a physics background can think of
this as being somewhat like special relativity -- the concept of
"before" is relative to the observer.)

> which, to me, means that the reference to the new object is not available
> for assignment until after the constructor has returned.

You might think that, but you're forgetting that you can't rely on 
things appearing in order across threads unless both synchronize on
the same object.  JLS 17 says: 
  - In the absence of explicit synchronization, a Java implementation
is free to update the main memory in an order that may be surprising.

Your interpretation of the JLS here is plausible, but I'm not convinced
that this is the only plausible interpretation of the spec.  Both this
and the below reference describe the required behavior as it appears to 
the executing thread (just like in the so-called "as-if-serial" behavior)
but does not mean that it has to appear that way to other threads in
the absense of synchronization.  

> Further, 15.5.21 Simple Assignment Operator = also clearly states that the
> rh operand is evaluated before the assignment, and if it completes abruptly
> the assignment does not take place.   So the JIT can't reorder for that
> reason alone (unless it can completely unroll the assignment, which is very
> hard in a multithreaded environment, so I'd bet it cannot).

By the way, there ARE compilers (e.g., Symantec) which generate code
like this: 
  a = new Foo();
as
  a = malloc(sizeof(Foo));
  a.init();
instead of
  temp = malloc(sizeof(Foo));
  temp.init();
  a = temp;

I think that this optimized implementation is not inconsistent with
15.5.21, as long as the assignment is rolled back if "new Foo()"
throws an exception.  The executing thread would never know the
difference, but other threads spying on the unreliable unsynchronized
memory side-effects might.  And that's OK, as far as the spec is
concerned.

> So I think your code and assumptions are perfectly reasonable!

Sorry, I don't agree.  With all due respect, I think your
interpretation is wishful thinking.

Being a performance weenie, I don't like it any more than you do.  But
in the absense of synchronizing any variable shared between threads,
you are taking a risk. 

Its also worth noting that Pugh has shown that no popular JVM properly
implements the JMM.  Many don't implement volatile correctly (or at
all.)  It may even be the case that under certain JVMs, that the code
discussed here works.  

Cheers,
-Brian


[ Original message follows ]

> To:   Sam Ruby/Raleigh/IBM@IBMUS
> cc:
> From: Mike Cowlishaw/UK/IBM@IBMGB
> Subject:  Re: Memory Model discussion  (Document link: Sam Ruby)
> 
> Not really -- there's a dup, and hence another reference to the object
> floating around.    So if the method call has a side effect on the object
> (and it is known to use the reference to the object) then it's unsafe to
> reorder.  [And of course it certainly does have a side efffect in this case
> :-)]
> 
> Have now had a chance to look at the Java Spec.; it does seem quite
> specific.   12.5 Creation of New Class Instances says:
> 
>    "Just before a reference to the newly created object is returned as the
>    result, the indicated constructor is processed...".
> 
> which, to me, means that the reference to the new object is not available
> for assignment until after the constructor has returned.
> 
> Further, 15.5.21 Simple Assignment Operator = also clearly states that the
> rh operand is evaluated before the assignment, and if it completes abruptly
> the assignment does not take place.   So the JIT can't reorder for that
> reason alone (unless it can completely unroll the assignment, which is very
> hard in a multithreaded environment, so I'd bet it cannot).
> 
> So I think your code and assumptions are perfectly reasonable!
> 
> Mike
> 
> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
> Mike Cowlishaw, IBM Fellow
> mailto:mfc@uk.ibm.com  --  http://www2.hursley.ibm.com
> 
> 
> Sam Ruby@IBMUS
> 01/27/2001 07:43 PM
> 
> To:   Mike Cowlishaw/UK/IBM@IBMGB
> cc:
> From: Sam Ruby/Raleigh/IBM@IBMUS
> Subject:  Re: Memory Model discussion  (Document link: MFC mail)
> 
> Interesting .. I wasn't aware that Java didn't define assignment as
> happening after the rhs is executed.   Sounds like a bug in the language
> definition to me -- not a 'memory model' issue at all :-)
> 
> Take a look at a small disassembled Java source:
> 
>    Method void main(java.lang.String[])
>       0 new #2 <Class test>
>       3 dup
>       4 invokespecial #3 <Method test()>
>       7 astore_1
>       8 return
> 
> I can see how, from a JIT perspective, there is an unrelated method call
> (at offsets 3 and 4) inserted in the middle of an assignment (offsets 0 and
> 7).
> 
> - Sam Ruby
> 
> 
>