14

I am currently reading JSR-133 (Java Memory Model) and I can't understand why f.y might be non-initialized (could see 0). Can someone explain it to me?

class FinalFieldExample {
    final int x;
    int y;
    static FinalFieldExample f;

    public FinalFieldExample() {
        x = 3;
        y = 4;
    }

    static void writer() {
        f = new FinalFieldExample();
    }

    static void reader() {
        if (f != null) {
            int i = f.x; // guaranteed to see 3
            int j = f.y; // could see 0
        }
    }
}
user123454321
  • 1,028
  • 8
  • 26
  • I know this relates to the JVM being allowed to reorder writes, but I don't know the details. :-| – T.J. Crowder Jul 04 '15 at 17:20
  • As i think you have not read jsr133 properly, that is clearly mentioned there.`Do not write a reference to the object being constructed in a place where another thread can see it before the object’s constructor is finished. If this is followed, then when the object is seen by another thread, that thread will always see the correctly constructed version of that object’s final fields` but for non-final fields thread can see default value. – Prashant Jul 04 '15 at 18:12
  • yes, this is insane. hopefully they'll fix it in java9. – ZhongYu Jul 04 '15 at 19:11
  • @bayou.io: Probably not, it's been this way a *long* time. And agreed, it's insane. – T.J. Crowder Jul 05 '15 at 06:33

3 Answers3

9

This is called a "premature publishing" effect.

Putting it simple, JVM is allowed to reorder program instructions (for performance reasons), if such reordering does not violate the restrictions of JMM.

You expect the code f = new FinalFieldExample(); to run like:

1. create the instance of FinalFieldExample
2. assign 3 to x
3. assign 4 to y
4. assign created object to variable f

But in provided code, nothing can stop JVM from instruction reordering, so it can run the code like:

1. create the instance of FinalFieldExample
2. assign 3 to x
3. assign raw, not fully initialized object to variable f
4. assign 4 to y

If reordering happens in a single thread environment, we won't even notice it. That's because we expect, that objects will be fully created before we start to work with them and JVM respects our expectations. Now, what can happen, if several threads run this code simultaneously? In next example Thread1 is executing method writer() and Thread2 - method reader():

Thread 1: create the instance of FinalFieldExample
Thread 1: assign 3 to x
Thread 1: assign raw, not fully initialized object to variable f
Thread 2: reading f, it is not null
Thread 2: reading f.x, it is 3
Thread 2: reading f.y, it is still 0
Thread 1: assign 4 to y

Definitely not good. To prevent JVM doing this, we need to give it additional information about the program. For this particular example, there are some ways to fix the memory consistency:

  • declare y as final variable. This will cause "freeze" effect. In short, final variables will be always initialized on the moment when you access them, if reference to the object was not leaked during construction.
  • declare f as volatile variable. This will create "synchronization order" and fix the problem. In short, instructions can't be reordered below volatile write and above volatile read. Assigning to f variable is a volatile write, that means new FinalFieldExample() instructions can't be reordered and executed after assignment. Reading from f variable is a volatile read, so reading f.x can not be executed before it. Combination of v-write and v-read is called synchronization order and provides the desired memory consistency.

Here is a good blog, that can answer all your questions about JMM.

AdamSkywalker
  • 11,408
  • 3
  • 38
  • 76
  • If the call to the constructor can happen after the reference to the object is published to `f`, how does a JVM satisfy _guaranteed to see 3_? – Sotirios Delimanolis Jul 04 '15 at 17:53
  • @SotiriosDelimanolis this is called "freeze action". In short, if there is a final field assign inside a constructor, JMM is forced to execute it before reference to new object will be published. – AdamSkywalker Jul 04 '15 at 17:56
  • If we had the `y = 4` before the assignment to the `final` field `x`, would this freeze action guarantee that `y` is also initialized? Or can a JVM reorder it to after `x`'s assignment and publish the object, leaving `y` potentially uninitialized? – Sotirios Delimanolis Jul 04 '15 at 18:00
  • @SotiriosDelimanolis Easy, the jit doesn't do such optimizations. Practically speaking (hotspot), there's a memory barrier put after the constructor if there's a final field which disallows the reordering. Actually that'll avoid the problem for all fields if there's one final field - purely an implementation detail, no need to do so (just easier to implement) – Voo Jul 04 '15 at 18:02
  • @SotiriosDelimanolis There's no direct answer to this question in documentation, but.. Once I found a blog post, that attempted to answer this particular question. There were lots of maths and assumptions, and finally it said that there is no guarantee, that it will work. So I suggest you to make Y final as well. – AdamSkywalker Jul 04 '15 at 18:03
  • @AdamSkywalker There is an actual write up that says otherwise. That is, if `x != null` then `y` will equal `4`. http://shipilev.net/blog/2014/safe-public-construction/. That is if `y=4` happens before the assignment of `x`. – John Vint Jul 04 '15 at 18:39
  • @JohnVint it says "That means this one **can be** a safe initialization given this simple VM implementation". I'm not sure what is definitly meant by this, but I'll assume it is not a real example, since it has a contradiction with an example from JSR. – AdamSkywalker Jul 04 '15 at 19:06
  • @JohnVint - no... for any memory location that can be navigated from a final field, any write to it before constructor exit will be visible to anyone who obtained the reference to the object... Say, a final field is a Map; any writes to the map in the constructor will be visible by others who sees the field, as expected. – ZhongYu Jul 04 '15 at 19:19
  • @JohnVint - however, it is highly improbably that a compiler can do such detailed analysis; it is likely that, as long as there is one final field, all fields will be treated as final too. – ZhongYu Jul 04 '15 at 19:23
2

The JVM may reorder memory reads and writes, so the reference to f may be written to main memory before the value of f.y. If another thread reads f.y between these two writes, it will read 0. However, if you create a memory barrier by writing a final or volatile field, reads and writes after the barrier cannot be reordered with respect to reads and writes before the barrier. So it's guaranteed that both f and f.y will be written before another thread reads f.

I asked a similar question here. The answers go into a lot more detail.

Community
  • 1
  • 1
Kevin Krumwiede
  • 9,868
  • 4
  • 34
  • 82
-1

Java memory model allows a thread to create a FinalFieldExample, initialize final x and save a reference to FinalFieldExample instance to f field before initializing non-final y.

Evgeniy Dorofeev
  • 133,369
  • 30
  • 199
  • 275