Fixing the Java Memory Model

Discussions

News: Fixing the Java Memory Model

  1. Fixing the Java Memory Model (35 messages)

    JSR 133, which has been active for nearly three years, has recently issued its public recommendation on what to do about the Java Memory Model (JMM). Several serious flaws were found in the original JMM. This article discusses the memory model, the flaws, fixes already in JDK 1.4, and what will be in 1.5.

    Excerpt

    "Several serious flaws were found in the original JMM, resulting in some surprisingly difficult semantics for concepts that were supposed to be simple, like volatile, final, and synchronized. In this installment of Java theory and practice, Brian Goetz shows how the semantics of volatile and final will be strengthened in order to fix the JMM. Some of these changes have already been integrated in JDK 1.4; others are slated for inclusion in JDK 1.5."

    Read Brian Goetz in Fixing the Java Memory Model, Part One

    Threaded Messages (35)

  2. Fixing the Java Memory Model[ Go to top ]

    Do I understand correctly that the issues arise because of SMPs and their cache-coherence protocols?

    Does someone know a good review of real-world SMP architectures?
  3. Fixing the Java Memory Model[ Go to top ]

    Do I understand correctly that the issues arise because of SMPs and their cache-coherence protocols?

    The article described two race conditions. The first, which he named "Problem #1", can occur on a uniprocessor.
  4. Fixing the Java Memory Model[ Go to top ]

    The article described two race conditions. The first, which he named "Problem #1", can occur on a uniprocessor.


    I looked at the code. It makes total sense that in the absence of synchronization even on a uniprocessor you have a problem. I didn't think that would fall under "memory model". It's rather a coding issue. The code isn't thread safe. What am I missing here .. ?

    Does this mean that the JSR also states how String should work, and this falls under the memory model?
  5. Fixing the Java Memory Model[ Go to top ]

    It makes total sense that in the absence of synchronization even on a uniprocessor you have a problem. I didn't think that would fall under "memory model". It's rather a coding issue. The code isn't thread safe.

    It isn't even a threading problem. I've been bitten by it in single threaded code. Variables declared final have an annoyingly visible unassigned state at the beginning of their life. The compiler happily compiles it, but NPE crash when later run:

    class Buzz {
      static final String buzzTwice=buzz+buzz; // NullPointerException crash.

      static final String buzz="bzzzzt";
    }

    I agree with you that the author has made mistakes in his explation of Problem #1. He claims that the problem "might not be possible on all JVMs or platforms", but it's actually universal. He also crypticly utters that "it was allowed by the old memory model specification." Does this mean that the problem is fixed in the new memory model? If so, how? I expected the author to cover this.
  6. Fixing the Java Memory Model[ Go to top ]


    > class Buzz {
    >   static final String buzzTwice=buzz+buzz; // NullPointerException crash.
    >
    >   static final String buzz="bzzzzt";
    > }
    >

    javac 1.4.1_01 would give you an "illegal forward reference" error for the above code.

    I don't understand how you can have access to an object before its constructor finish its job. Say, in the substring example, how do you get a reference to the new string before its constructor return?
  7. Fixing the Java Memory Model[ Go to top ]

    javac 1.4.1_01 would give you an "illegal forward reference" error for the above code.

    Ooops. I intended a different example. I just tested it, and it compiles and crashes when run:

    public class Race {

      public static void main (String args[]) {
        new Race();
      }

      final String field;

      Race () {
        crash();
        field="ok";
      }

      void crash () {
        field.getClass(); // NullPointerException
      }
    }
  8. This is ok.[ Go to top ]

    It crashes because you are accessing a null reference. Strings are initialized to null so you get that exception if you invoke a method on it before you assign a valid object to that reference.

    The problem would be if somene got an instance of Race and then called crash() and got a null pointer.
  9. This is ok.[ Go to top ]

    It crashes because you are accessing a null reference. Strings are initialized to null so you get that exception if you invoke a method on it before you assign a valid object to that reference.

    Yes, but this is exactly what the author's Problem #1 is demonstrating. Or in his words a program can "see one value for an immutable object, and then at some later time see a different value". The audience inferred that this problem is peculiar to multi threading, but it isn't. I posted a single threaded program that exhibits the author's complaint: an immutable variable read before its initialization completes.
  10. The problem is that you cannot be sure that instance variables have
    been initialized until after the constructor has finished. So
    any attempt to access instance variables is fraught if the constructor set it.

    Look at class Managed. Safe as houses, right?
    class Managed{
      public Managed() {
        Manager.getInstance().register(this);
       }
       public int getFoo() { return 6;};
    }

    Then look at class B, which really is fine.
    class B extends Managed{
      final int foo;
      B(int aFoo){
         this.foo = aFoo;
      }
      public int getFoo () {
         return this.foo;
      }
    }

    Now suppose that Manager.register(...) calls getFoo. Or Manager passes the object reference on to something else that calls getFoo. A nasty little bug.

    But I think Brian's article is confusing in that as far as I can tell this is still a bug after JSR133 goes in.

    Basically, you should never give away a reference to yourself during construction, and avoid naive use of the GoF 'Builder' pattern as well, and JSR 133 won't save you from that.

    Sean
  11. Example is incorrect[ Go to top ]

    public class Race {

      public static void main (String args[]) {
        new Race();
      }

      final String field;

      Race () {
        crash();
        field="ok";
      }

      void crash () {
        field.getClass(); // NullPointerException
      }
    }

    That code is incorrect and no JSR is going to fix that. The order of initialization is incorrect in the constructor. The following code below works.

    public class Race {

      public static void main (String args[]) {
        new Race();
      }

      final String field;

      Race () {
        field="ok";
        crash();
      }

      void crash () {
        field.getClass(); // NullPointerException
      }
    }
  12. Fixing the Java Memory Model[ Go to top ]

    class Buzz {

    > static final String buzzTwice=buzz+buzz; // NullPointerException crash.
    >
    > static final String buzz="bzzzzt";
    > }

    Okay. I didn't catch this in his explanation. I thought he was talking about internals of java/lang/String.java. There's nothing final in there ...
     
    I don't understand what the scope of a "memory model" should be, but this is from the Java Language Specification:

    http://java.sun.com/docs/books/jls/second_edition/html/classes.doc.html#287406

    8.3.2.3 Restrictions on the use of Fields during Initialization
    [..]
    These restrictions are designed to catch, at compile time, circular or otherwise malformed initializations. Thus, both:

    class Z {
    static int i = j + 2;
    static int j = 4;
    }

    and:

    class Z {
    static { i = j + 2; }
    static int i, j;
    static { j = 4; }
    }

    result in compile-time errors.
  13. Fixing the Java Memory Model[ Go to top ]

    Hi Brian,

    Regarding your sample:

    class Buzz {
      static final String buzzTwice=buzz+buzz; // NullPointerException crash.

      static final String buzz="bzzzzt";
    }

    I think this error is in fact a fault of JLS, because does not specify the order in which compile time constants are initialized. It only specifies that, for instance, static initializers are executed "in textul order" - which I assume that is the order it appears in the source file. And also, it can only be assumed, based on JSL, that static fields are also initialized in "textual order".

    "
    2.17.5
    8. Next, execute either the class variable initializers and static initializers of the class or the field initializers of the interface, in textual order, as though they were a single block, except that final static variables and fields of interfaces whose values are compile-time constants are initialized first.
    "

    I suppose that compiler vendor made this assumption, and that is why the compiler breaks. If my logic is true, if you have

    class Buzz {
      static final String buzz="bzzzzt";
      static final String buzzTwice=buzz+buzz; // should not be NullPointerException crash.

    }

    should compile.

    Cristian
  14. Fixing the Java Memory Model[ Go to top ]

    I think the author may have missed a vital point in the explanation of problem #1, which is why it seems like "it's not even a memory model problem at all". There are two aspects to this problem: prescient writes and stale cache values.
    Naively one would think that no thread can get a reference to the string object while it is being construced (because the constructor only returns the reference when it is done). Thus when another thread does get the reference, the String object must already be properly initialized by the constructor. This is not necessarily true when the VM uses an optimization called "prescient writes" which allows it to write the value that the constructor would return before it actually returned it. As I recall the symantec JIT did this even in uniprocessor machines. If a reference is written before the constructor returns then Strings cannot be assumed to be immutable.
    The other problem is with stale cache values. Even if the constructor did finish running and updated all of the String fields, another thread may read some of the fields from it's cache - seeing old values, and other fields from main memory - seeing new values.

    If you want to see the nontrivial impliciations of this problem consider this code:

    void deleteFile(String fileName) {
       if (!fileName.startsWith("/tmp")) throw new IllegalArgumentException();
       new File(fileName).delete();
    }

    To most developers this code would seem safe. Even the security experts who wrote Java's security framework used this kind of code in the past. The code is actually unsafe, because the fileName reference may point to a String that was created by some other thread, and whose value may appear to change during the execution of the method. Particularly it may appear to be "/tmp/etc" when running the first line (the condition), but then switch to "/etc" when running the second line.

    As for problem #2, it may not occur in practice on uniprocessor machines, but the point is that the JMM allows it to occur (even on uniprocessor machines; the JMM does not make a distinction between multiprocessor and uniprocessor machines). A VM can simply decide to update initialized before updating all the other fields.
    Since the idiom described in problem #2 is useful in many situations and no feasible alternative has been found that works on the current JMM (except forcing a memory barrier or using a ThreadLocal, both of which do not perform very well) I think it can definately be considered a "problem".
  15. Fixing the Java Memory Model[ Go to top ]

    Gal,

    PMI but how can that happen? How can a String object appear to be mutable?

    Peace,

    Cameron Purdy
    Tangosol, Inc.
    Coherence: Clustered JCache for Grid Computing!
  16. Fixing the Java Memory Model[ Go to top ]

    Cameorn,

    It can occur when one thread accesses a String object constructed by another thread. For instance:

    Thread1:
    char[] chars = {'/','t','m','p','/','e','t','c'};
    globalString = new String(chars,4,4);

    Thread2:
    deleteFile(globalString);

    Assuming Thread2 did not flush it's cache by issuing a monitorenter sometime prior to calling deleteFile(globalString) (but after Thread1 constructed globalString) then some of the fields of the globalString may be read from the cache. For instance the offset may be seen as zero, it's default value, rather than 4, the value assigned by the String constructor. So globalString appears to be "/tmp". At any point in the future the VM may flush the cache and see the new offset value 4, so globalString appears to be "/etc".

    Gal
  17. Assuming Thread2 did not flush it's cache by issuing a monitorenter sometime prior to calling deleteFile(globalString)...

    Yes but, as Guglielmo noted early on, this logic obviously belongs in a critical section regardless of memory model. Now I'm really curious. Just how exactly has the memory model been revised to solve this? Or is it still a problem even after revising the memory model? The author never addressed it.
  18. Yes but, as Guglielmo noted early on, this logic obviously belongs in a critical section regardless of memory model.


    To most people it is not at all obvious that the value of a String may change. In fact people explicitly assume that Strings are immutable all the time, and the possibility of mutable Strings poses a serious security risk. Therefore I think that the spec should make sure that immutable objects never change value, and if the current spec does not enforce this then it should be revised.

    > Now I'm really curious. Just how exactly has the memory model been revised to solve this? Or is it still a problem even after revising the memory model? The author never addressed it.

    From the quick look I had at the new spec, it seems that the spec ensures the following: assuming the object is "properly constructed" in the sense that the constructor does not "leak out" references to the instance being constructed before construction is finished, then all threads will see the correct updated values of all final fields of the instance. Additionaly, if those final fields are references, then all threads will see values of these references that are at least up to date as of the end of the constructor. Thus you can assume that whenever a thread reads the immutable part of an object, it sees the values assigned by the constructor (again, assuming the constructor itself does not "leak out" references before completing the construction).

    Gal
  19. From the quick look I had at the new spec, it seems that the spec ensures the following: assuming the object is "properly constructed" in the sense that the constructor does not "leak out" references to the instance being constructed before construction is finished, then all threads will see the correct updated values of all final fields of the instance.

    So with this revised memory model, would the individual developer need to declare critical sections to achieve constructor atomicity or not?
  20. From the quick look I had at the new spec, it seems that the spec ensures the following: assuming the object is "properly constructed" in the sense that the constructor does not "leak out" references to the instance being constructed before construction is finished, then all threads will see the correct updated values of all final fields of the instance.

    >
    > So with this revised memory model, would the individual developer need to declare critical sections to achieve constructor atomicity or not?

    It depends on what you mean by constructor atomicity.

    The new memory model offers the guarantee that all threads will only see the final initialized value for a final field of an object. This means that it's impossible to see half the final fields as initialized and the other half as unitialized (which is what happened in the String example given in the article). In this sense, it's atomic.

    But there are two catches:
    + First, your code must ensure that under NO circumstances can another thread access your object (directly or indirectly) before the constructor completes.
    + However, this guarantee doesn't apply to non-final fields. If you want to get atomicity and isolation in your reads/writes to non-final fields, you need a critical section for both the reader and the writer threads.

    The first issue is the really throny one. There are many ways in which another thread might get a reference to an object before that object's constructor runs.
    A) One scenerio is where instruction reordering causes the return value from new() to "leak"... Consider this code...
       static Foo f = new Foo();
    A compiler might translate this into something like the following psyudo-bytecode:
       static Foo f = malloc(sizeof(Foo));
       f.executeConstructorAndInitialize();
    In this scenerio, if another thread accessed f between the assignment of f and the completion of the constructor, then f's final variables may not have been initialized. The new changes to the JVM should forbid this in situations where that object reference could lead to the premature reading of a final variable (but no such guarantee is made for non-final variables).

    B) Another scenerio is where the object's constructor (or field initializers) passes a reference to "this" to another object that is in turn accessable by other threads. Once a thread gets a copy of a final variable (initialized or not), that thread is allowed to retain a cached copy of that variable INDEFINITELY. To make matters worse, a thread make copies of that cached uninitialized final varibable, or even assign reference to other objects which could then be picked up by other threads which in turn could cache a state value INDEFINITELY.

    Here are two best practices to avoid scenerio B, since avoiding this the responsability of the programmer:
    + An object's constructor should generally avoid either directly or indirectly doing anything which results in assigning "this" to anything which exists outside of the object. Rather, do this "registration" in a seperate step after the constructor finishes. (And in the cases where you absolutely feel you must break this rule, make sure you don't use final variables.)
    + An object's constructor should never call non-final methods on it's own class (as subclasses of this object could change the behavior of that method which might break assumptions about final variables).

    Doug
  21. Fixing the Java Memory Model[ Go to top ]

    Thread1:

    > char[] chars = {'/','t','m','p','/','e','t','c'};
    > globalString = new String(chars,4,4);
    >
    > Thread2:
    > deleteFile(globalString);

    It's very comforting that this example is somewhat unlikely. In reality, this would actually be a race condition, i.e. you have the even bigger problem that globalString could just be null (too early). Since you can't have that, programmers would use synchronized and notify(). Or else, even more likely, globablString would be passed as a parameter by the same thread which created it, in which case the constructor will have terminated.

    In any case, I think Doug Lea in his article makes a great case for specifying the behavior of the CPU. It's also consistent with the general philosophy of Java which is to provide a complete virtual machine specification.

    So, on a practical level, which parts of the CPU are affected? I can see that the cache is one, and maybe the pipeline is another? Any more?
  22. Fixing the Java Memory Model[ Go to top ]

    It's very comforting that this example is somewhat unlikely. In reality, this would actually be a race condition, i.e. you have the even bigger problem that globalString could just be null (too early). Since you can't have that, programmers would use synchronized and notify().


    This particular scenario is considered to be more of a security risk than a programmer error risk. A malicious piece of code could attempt to create a "mutable String" on purpose, in order to bypass a security check. For example, in the code I demonstrated in my first post the mutable String could be used to delete files in "/etc" even though the code is only supposed to allow deleting files in "/tmp". This can be fixed by making the deleteFile method synchronized (this forces flushing the cache). However, having to make this method synchronized is counter-intuitive. Developers should be able to assume that immutable objects are immutable without introducing synchronization.

    > Or else, even more likely, globablString would be passed as a parameter by the same thread which created it, in which case the constructor will have terminated.

    Even if the constructor did terminate, a seperate thread that accesses the String object and did not perform synchronization can still see stale values. In practice an object should rarely move from one thread to another without synchronization. In the cases where this is done extreme caution is required. But like I said, this particular scenario is a security risk, not a bug risk.


    The JMM doesn't define the behavior of specific parts of the CPU. Doing so would probably lock Java to a particular CPU architecture. Instead, the JMM simply defines a set of rules that any compliant VM must follow. The VM can run on any CPU and use whatever implementation strategy it wants, as long as the rules are not violated. Particularly, the word "cache" in the context of the JMM does not necessarily mean the CPU's cache memory. A variable can also be cached in a register, or on the stack, or wherever the VM wants to cache information. The thread's "cache" is just a logical indirection designed to allow VMs not to perform every memory action directly to the main memory.

    Gal
  23. Fixing the Java Memory Model[ Go to top ]

    The thread's "cache" is just a logical indirection designed to allow VMs not to perform every memory action directly to the main memory.


    Agreed. But in the end it would still be interesting to see how the rules affect real machines.
  24. Fixing the Java Memory Model[ Go to top ]

    We need a unified java memory model so that a java programmer can
    write proper code without knowing all different platform specific
    memory model (including SMP platforms).

    The "serval serious flaws" is not SMP specific. Rather it is
    flaws in the jmm itself. In some case, following the jmm doesn't
    guarantee race-free program. In some other, it is too restrictive
    for a JVM to follows.
  25. Fixing the Java Memory Model[ Go to top ]

    This may be a dumb question, but...

    Why *didn't* they decide to fix the double-check locking problem?

    This seems to be one of the most well-known shortcomings of the JMM, and from a laymen's perspective, double-check locking seems like it *should* work... that is, until you find out about all the craziness, reordering instructions, etc. that is allowed for in the spec...

    The article didn't really provide any detail here... other than to say its still broken, the volatile hack *will* work, but we discourage you to use it anyways...

    -Tim.
  26. Fixing the Java Memory Model[ Go to top ]

    This may be a dumb question, but...

    >
    > Why *didn't* they decide to fix the double-check locking problem?

    Because double-check locking isn't viewed as a problem, but rather as an anti-pattern (or anti-idiom). Should the designer's of Java design Java so that people can use antipatterns without hanging themselves?

    The trick is remembering your objective.... The objective is to get fully initialized objects from the "writer" thread without having to synchronize the "reader" thread. Double-check locking was a failed attempt at doing this. The key issue here is the changes to the Java memory model provide a new way to do this without having to resort to nasty, broken hacks like double-check locking. (For the details, wait until part 2 of the article comes out, or if you're impatient, read the memory model spec for Java 1.5.)

    As a reminder for those who might be reading this thread the first time, here's what double check locking is all about:

    /* This is an J2EE example of a JNDI lookup helper class*/
    public class NamingHelper{
       public Context namingContext;
       public Context getNamingContext() {
          if (namingContext != null)
             return namingContext;
          synchronized(this) {
             if (namingContext != null)
                namingContext == new InitialContext();
             }
          }
       }
    }

    The intent of such code was to avoid the synchronized block if namingContext had already been initialized. The double check inside the synchronized block is used to ensure there isn't a race condition where the object gets initialized twice.

    Double-check locking will indeed insure namingContext doesn't get initalized twice. The failing comes in the fact that the reader thread may never synchronize before using namingContext. This means that the reader thread never gets it's cache flushed and could end up using an invalid copy of the InitialContext object. This would be very bad.

    The lesson: Don't use double-check locking because it doesn't solve the problem that it sets out to use. Instead use a design pattern that DOES solve the problem. Here are two:

    //Option 1, synchronize your mutable shared variables
    public class NamingHelper{
       public Context namingContext;
       public synchronized Context getNamingContext() {
          if (namingContext != null)
             namingContext == new InitialContext();
          }
          return namingContext;
       }
    }

    //Option 2, make the variable immutable
    public class NamingHelper()
    {
       private static final Context namingContext = new InitialContext();
       public static Context getNamingContext() {return namingContext;}
    }
  27. double check[ Go to top ]

    Does anybody know if double check works with the JDK 1.4 JMM or JDK 1.5 JMM?

    By double check I mean:

    if (x == null) {
       synchronized(this){
          if (x == null) x = new Object();
       }
    }
  28. double check[ Go to top ]

    No, double-checked locking does not work in either of the JMMs. In the new JMM double-checked locking does work if you make "x" volatile. However, the rules of the new JMM effectively force the VM to flush it's cache when accessing a volatile field, so it becomes almost as expensive as using synchronized code. Anyway you can compare the performance yourself when we have a compliant VM (I'm not sure if the 1.5 VM is compliant, anybody?)

    Gal
  29. double check[ Go to top ]

    In the new JMM double-checked locking does work if you make "x" volatile. However, the rules of the new JMM effectively force the VM to flush it's cache when accessing a volatile field, so it becomes almost as expensive as using synchronized code.

    That's a nice improvement for SMP. Surely correct behavior is worth the added latency.
  30. double check[ Go to top ]

    In a multi-processor machine, the use of a volatile in the new memory model is still going to be significantly faster than the use of a synchronized block. The critical issue is that processors won't have to vie for the lock, which is a particularly complex operation for multi-processor systems. Rather, the processors using the variable simply flush their cache, which isn't cheap, but is still almost an full order of magnitude faster. Of course, we really won't know exactly how much faster until the JVM's ship and benchmarks are gathered.

    What I'm really looking forward to is the introduction of java.util.concurrent. It's high time we had higher level primatives for coordination multithreaded execution for precisely the reason that writing good threadsafe code at such low levels is indeed very hard to do.
  31. double check[ Go to top ]

    By double check I mean:

    >
    > if (x == null) {
    > synchronized(this){
    > if (x == null) x = new Object();
    > }
    > }

    It's the first time I see this. Usually I just use the (synchronized) single check. I think synchronization is pretty optimized these days, so I think putting this in for most people would premature optimization (I am not telling Bill this, I am just using the example he posted.)

    And since it doesn't work, now I am definitely never going to use it ..

    I briefly wondered if it might not be made safer by checking x!=null instead, but then the compiler can re-arrange this type of control-flow stuff.

    I'd say it's more trouble than it's worth ..
  32. Mutable Immutable Fields[ Go to top ]

    The article points out that in the older Java Memory Models, other threads might see a "half-constructed" object under certain race conditions. If I'm reading this right, the following code could also break?

    public class NamingHelper()
    {
       private final Context namingContext = new InitialContext();
       public Context getNamingContext() {return namingContext;}
    }

    Under some race conditions, namingContext could return null?

    Under the old memory model (and the new), we know the constructor itself is synchronized, so we know initializations made in the constructor will get written out to main memory from the cache when the constructor complete. However, if the "consumer" thread calling getNamingContext() doesn't synchronize, it's possible it's reading stale cached data rather than this copy from main memory.

    This, of course, is quite disconcerting, as the immutable field approach has long been used as a means of achieving thread saftey without requiring extra synchronization. So if I'm wrong here in assuming this race condition could occur, someone please tell me (and explain why).

    (Reminder: Java states the following order for object construction:
    + First all memory allocated to the object is initialized with nulls. (ie all fields are initalized to null. The exception to this is fields with primative constants, which can be initialized to their compile time values.)
    + The base class is initialized.
    + Field are initialized to their initial values (ie field initializers run).
    + The constructor is run.


    Next question, if I make the methods static, does the same thing happen?

    public class NamingHelper()
    {
       private static final Context namingContext = new InitialContext();
       public static Context getNamingContext() {return namingContext;}
    }

    I believe this is safe (whereas the earlier example is not) in that getNamingContext() will never return null. The reason is that when a Thread uses a class object for the very first time, it would naturally need to check if the class has already been loaded and initialized. Even the early Java specs very explicitly state that class initialization must be synchronized. The use of the synchronized construct will in turn insure that any memory cache is flushed and fresh copies of the data are read from main memory.

    After pouring through "The Java Language Specification, 2nd Edition (June 2000)," I was unable to find an explicit statement that a thread MUST ALWAYS perform this synchronized check when first accessing a class, even when it knows for a fact that class object has already been initialized by another thread. This leaves open the possability that a thread knows the class has been initialized but doesn't flush it's cache upon first use to ensure a fresh copy of static fields. (ie, this exaclty the same problem as double-check locking... you know the object was initialized but you forget to ensure your thread's cache is aware of those initialized values.)

    I know this is coming across as a novice question. But trust me, it's not. Classicly we've always believed this was okay. But a precise understanding of EXACTLY what guarantees earlier versions of Java make is really important here. Heisenbugs caused by race conditions are notoriously hard to pin down.

    (Enter tech support's party line: I'm sorry sir, but if this was a bug in our software, don't you think other people would have been having this problem too? We just can't reproduce it. It must be a problem with your PC.)

    Doug
  33. Mutable Immutable Fields[ Go to top ]

    <doug>
    The article points out that in the older Java Memory Models, other threads might see a "half-constructed" object under certain race conditions. If I'm reading this right, the following code could also break?

    public class NamingHelper()
    {
       private final Context namingContext = new InitialContext();
       public Context getNamingContext() {return namingContext;}
    }

    Under some race conditions, namingContext could return null?
    </doug>

    Yes.

    <doug>
    Under the old memory model (and the new), we know the constructor itself is synchronized, so we know initializations made in the constructor will get written out to main memory from the cache when the constructor complete. However, if the "consumer" thread calling getNamingContext() doesn't synchronize, it's possible it's reading stale cached data rather than this copy from main memory.
    </doug>

    Constructors are not synchronized by default, and can't be made synchronized. The JLS claims that synchronized constructors are not allowed because they would have no practical use as they would lock the object being constructed, which is normally not made available to other threads. It ignores the fact that synchronized methods have an additional effect on the cache of a thread which can be useful in the context of a constructor (to force the constructor to write it's updates to main memory). The restriction might also have something to do with not wanting to create the impression that the synchronization begins before the call to the superclass constructor (synchronizing on an object *before* it has been constructed by the Object constructor might be tricky).
    You can force synchronization (after the call to the superclass constructor) using a synchronized block. However, as you noted, the "consumer" must also synchronize in order to see the correct values.

    <doug>
    This, of course, is quite disconcerting, as the immutable field approach has long been used as a means of achieving thread saftey without requiring extra synchronization. So if I'm wrong here in assuming this race condition could occur, someone please tell me (and explain why).
    </doug>

    It can occur under the assumption that the "NamingHelper" instance was created by one thread and then passed to another thread without synchronizing.
    The new JMM changes the semantics of final fields so that it would be impossible to see stale values of a final field unless the constructor itself "leaks" a reference to the object being constructed (in which this is a programming error, not a memory-model problem).

    <doug>
    Next question, if I make the methods static, does the same thing happen?

    public class NamingHelper()
    {
       private static final Context namingContext = new InitialContext();
       public static Context getNamingContext() {return namingContext;}
    }

    I believe this is safe (whereas the earlier example is not) in that getNamingContext() will never return null. The reason is that when a Thread uses a class object for the very first time, it would naturally need to check if the class has already been loaded and initialized. Even the early Java specs very explicitly state that class initialization must be synchronized. The use of the synchronized construct will in turn insure that any memory cache is flushed and fresh copies of the data are read from main memory.

    After pouring through "The Java Language Specification, 2nd Edition (June 2000)," I was unable to find an explicit statement that a thread MUST ALWAYS perform this synchronized check when first accessing a class, even when it knows for a fact that class object has already been initialized by another thread. This leaves open the possability that a thread knows the class has been initialized but doesn't flush it's cache upon first use to ensure a fresh copy of static fields. (ie, this exaclty the same problem as double-check locking... you know the object was initialized but you forget to ensure your thread's cache is aware of those initialized values.)
    </doug>

    It is generally agreed that this code is safe in the sense that all VMs will run such code correctly. However, there is some disagreement on whether the VM spec actually requires this code to run correctly. The right place to look is sections 12.4.2 and 12.4.3 of the JLS. Generally it sais that a class must perform the initialization procedure (including synchronizing on the Class instance) unless it knows that the class has already been initialized. It not completely clear whther this means that *this thread sees an initialized copy*, or just that the copy in the main memory is initialized.

    There is also the matter of which opcodes require the initialization procedure to run. Currently only the NEW, GETSTATIC, PUTSTATIC and INVOKESTATIC opcodes require that the VM verify that the class is initialized (and thus perform synchronization). This can lead to a situation where a static initializer of Class1 changes Class2.a, and then an instance method of Class1 reads Class2.a and gets stale values (assuming that the instance was created by a different thread - otherwise the NEW opcode would require initialization).

    Somebody suggested, I think it was Bill Pugh, the following names:
    "Weak class initialization safety" - current requirements.
    "Strong class initialization safety" - add INVOKEVIRTUAL and INVOKESPECIAL to the list of requirements.
    Also, to clarify the unclear point above, it would be stated that a thread may skip the initialization procedure only if the same thread has already performed the procedure (this ensures that all the writes made by the class initializers would be seen by the thread).
    I'm not sure what the expert group working on the JMM decided to do about this. I don't see a mention of these issues in the public review draft. You can probably find the extensive discussions on this topic in the JMM mailing list archive.

    <doug>
    I know this is coming across as a novice question
    </doug>

    Not at all, this is a very tricky subject.

    Regards
    Gal
  34. Mutable Immutable Fields[ Go to top ]

    <Gal>
    > There is also the matter of which opcodes require the initialization procedure to run. Currently only the NEW, GETSTATIC, PUTSTATIC and INVOKESTATIC opcodes require that the VM verify that the class is initialized (and thus perform synchronization). This can lead to a situation where a static initializer of Class1 changes Class2.a, and then an instance method of Class1 reads Class2.a and gets stale values (assuming that the instance was created by a different thread - otherwise the NEW opcode would require initialization).
    </Gal>

    If the instance of Class1 running reads Class2.a, this would translate to the GETSTATIC opcode. Therefore, even if the thread2 hadn't done the synchronized class initilization check, it would do so that this point. What's causing the read of stale data? (Of course, the larger issue in this example is that for Class1 to change Class2.a then a isn't final, and thus access to such a variable should be synchronized anyway, but we can set that aside for the sake of argument since we're just talking class initialization.)


    Here's another point of confusion that's come up while reading the new JMM draft spec. There seems to be an implicit assumption that the only way an object can be loaded into a thread's cache is if a reference the object is directly dereferenced in some way. Hence, by preventing the reference to an object from being "leaked" prior to completion of a constructor, the assumption is that thread's can never get a cached copy of the uninitialized state.

    However, is it really a reliable assumption that the only way an object can be drawn into a thread's cache is by being dereferenced? It's natural that in some hardware architectures, a processor may choose to bring in a full page of memory into it's CPU cache rather than caching on an object by object basis. In an situation like this, it could be possible to cache stale data for an object without it ever having been directly referenced prior to completion of the constructor.

    In the traditional Java memory model this wasn't an issue because the reader thread bore the responsability of using critical sections to ensure a clean copy of the data. But in the new memory model we're adding the requirement that reader threads see only the initialized copy of a final variable WITHOUT needing to constantly synchronize or flush the cache.

    On a multiprocessor system where each processor has it's own cache, how is the JVM going to meet this requirement? Should the JVM instead carefully track which final field's it's read before, and then flush the cache EVERYTIME it accesses a new final field it hadn't yet used? Talk about overhead!!!
  35. cache coherency[ Go to top ]

    Doug: It's natural that in some hardware architectures, a processor may choose to bring in a full page of memory into it's CPU cache rather than caching on an object by object basis. In an situation like this, it could be possible to cache stale data for an object without it ever having been directly referenced prior to completion of the constructor. [...] On a multiprocessor system where each processor has it's own cache, how is the JVM going to meet this requirement? Should the JVM instead carefully track which final field's it's read before, and then flush the cache EVERYTIME it accesses a new final field it hadn't yet used? Talk about overhead!!!

    Snoop.

    The "JVM" would not be able to see stale data in the processor cache. That is the job of the CPU to ensure -- it's called "cache coherency." (Some architectures will allow the stale cache to be used, but will rewind the processor if it has accidentally used stale cache, so the result is the same as if it never saw it.)

    The process of asynchronously (or even predictively) figuring out coherency issues is called snooping, and usually is provided by a special snooping protocol on a bus that runs among the multiple CPUs in an SMP system.

    Peace,

    Cameron Purdy
    Tangosol, Inc.
    Coherence: Clustered JCache for Grid Computing!
  36. Mutable Immutable Fields[ Go to top ]

    <doug>
    If the instance of Class1 running reads Class2.a, this would translate to the GETSTATIC opcode. Therefore, even if the thread2 hadn't done the synchronized class initilization check, it would do so that this point. What's causing the read of stale data? (Of course, the larger issue in this example is that for Class1 to change Class2.a then a isn't final, and thus access to such a variable should be synchronized anyway, but we can set that aside for the sake of argument since we're just talking class initialization.)
    </doug>

    GETSTATIC only synchronizes if the current thread hasn't tried to initialize Class2 before. If the thread has already accessed Class2, but hasn't accessed Class1, stale data can be read.
    My point, which has nothing to do with final fields. Class initialization safety means that a thread that uses a class can assume that the class initializers have run and that the changes made by the initializer are visible to it without performing synchronization. All writes made by the static initializers should be visible to a thead that uses the class, even if the updated fields are non-final.