30

I am trying to simply test out the initialization safety of final fields as guaranteed by the JLS. It is for a paper I'm writing. However, I am unable to get it to 'fail' based on my current code. Can someone tell me what I'm doing wrong, or if this is just something I have to run over and over again and then see a failure with some unlucky timing?

Here is my code:

public class TestClass {

    final int x;
    int y;
    static TestClass f;

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

    static void writer() {
        TestClass.f = new TestClass();
    }

    static void reader() {
        if (TestClass.f != null) {
            int i = TestClass.f.x; // guaranteed to see 3
            int j = TestClass.f.y; // could see 0

            System.out.println("i = " + i);
            System.out.println("j = " + j);
        }
    }
}

and my threads are calling it like this:

public class TestClient {

    public static void main(String[] args) {

        for (int i = 0; i < 10000; i++) {
            Thread writer = new Thread(new Runnable() {
                @Override
                public void run() {
                    TestClass.writer();
                }
            });

            writer.start();
        }

        for (int i = 0; i < 10000; i++) {
            Thread reader = new Thread(new Runnable() {
                @Override
                public void run() {
                    TestClass.reader();
                }
            });

            reader.start();
        }
    }
}

I have run this scenario many, many times. My current loops are spawning 10,000 threads, but I've done with this 1000, 100000, and even a million. Still no failure. I always see 3 and 4 for both values. How can I get this to fail?

palacsint
  • 28,416
  • 10
  • 82
  • 109
sma
  • 9,449
  • 8
  • 51
  • 80
  • 1
    Good question, I've been trying to get similar examples to fail due to unsafe initialization as well, but with no luck. It would be great if someone provided an example which actually fails. – axel22 Jan 26 '12 at 14:42
  • 4
    if you wish to test the JVM for final fields you should run on something that's not x86/x64 as the hardware memory model is too strong. – bestsss Mar 10 '13 at 18:56
  • @bestsss I understand that the goal is to get a run where `y` is 0. I would have thought that even on x86, introducing some padding to try to put `y` on a different cache line could lead to that situation (but I could not make it work, even after adding 7 longs betwen x and y)... – assylias Mar 11 '13 at 07:22
  • To be more specific about the bounty: I'd like to see a test which fails or an explanation **with some references** why it's not possible with current JVMs. – palacsint Mar 11 '13 at 07:38
  • 1
    @assylias, different cache lines will be propagated/made consistent in the same way/order they are written (TotalStoreOrder), i.e. the write to 'y' will predate the write to 'f'. You need a semi-buggy compiler that writes to 'f' the unintialized object first and then calls the c-tor... fat chance. One of the reasons 'f' is not written is a possible error in the c-tor, technically even the c-tor call can cause stack overflow (unless inlined and no safe point generated). – bestsss Mar 11 '13 at 08:19
  • 1
    @palacsint, here is the reference for you, read it - it shall be enough. To put it simply: a compliant JVM has to ensure that. I think the torture tests for jsr166 include a test: https://github.com/shipilev/java-concurrency-torture/blob/master/src/main/java/org/openjdk/concurrent/torture/tests/singletons/FinalWrapperSingletonTest.java :: The official Oracle JLS: http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5 :: Another reference by J. Manson who wrote the spec : http://jeremymanson.blogspot.com/2008/12/benign-data-races-in-java.html – bestsss Mar 11 '13 at 08:27
  • @bestsss I agree, I would be surprised to see it fail on x86: the memory model is too strong. – Lorenzo Dematté Mar 11 '13 at 16:24
  • Thread creation takes a lot of time, and the execution of reader/writer is fast, so there is a little chance they would collide. Instead of creating many threads, create 2 (or how many cores you have (no sense to run the test on single core)), and run reader (writer) in a long loop. Of course do not do anything (e.g. println) if the situation being caught does not happen. – Alexei Kaigorodov Mar 11 '13 at 17:10
  • Is it intended that you just read the values from one single instance 10.000 times after creating so many? – Martin Seeler Mar 12 '13 at 08:20
  • @bestsss: The linked JLS spec contains a similar example with `int j = f.y; // could see 0`. (Example 17.5-1. final Fields In The Java Memory Model) I'm still confused. I guess it's not required by the JVM specification but the current implementations ensures it. I'd happily accept some code from a JVM implementation with an explanation too. – palacsint Mar 12 '13 at 23:29
  • @palacsint, read the spec *again* and especially the example `y` is not final AT ALL - hence the default value. The spec is very, very clear. – bestsss Mar 13 '13 at 10:52
  • @bestsss: If I'm right that's the `y`˛which **could** be seen as 0 (`int j = f.y; // could see 0`). The question is: how? Is there any code which see `y` as 0? (It seems that the code in the question can't reproduce that.) (I didn't have time now to check the spec again but I will do it later.) – palacsint Mar 13 '13 at 11:20
  • @palacsint, *y is not final* - and YES all instantiated Java objects have their fields to the default values -> 0 for int (null for references, false for boolean, etc). Since the object is accessed via a race, it's possible to see the initial value. You might wish to read about memory models/cache coherency and especially JMM (java memory model), the topic is vast and far far beyond the scope of this question (and the associated bounty), I am afraid out of the scope of StackOverflow. – bestsss Mar 13 '13 at 11:36
  • use Thread.yield() when needed, it may improve your chances of seeing a failure – Dariusz Mar 14 '13 at 09:07
  • @palacsint If you can still award your bounty - you might want to give it to [the latest answer](http://stackoverflow.com/a/15517168/829571). If not you can still upvote ;-) – assylias Mar 20 '13 at 23:18
  • A sufficiently good compiler might notice that `this` never leaks the constructor and `x` and `y` are never written outside the constructor, so they can be replaced with constants (at least if your code does not use reflection). – tc. Jul 14 '13 at 14:16
  • @bestsss `x86` and Intel is the last piece in the puzzle that could optimize the code and re-order. JVM [can do that too. lately](https://stackoverflow.com/a/59385508/1059372) – Eugene Dec 18 '19 at 12:38

9 Answers9

20

I wrote the spec. The TL; DR version of this answer is that just because it may see 0 for y, that doesn't mean it is guaranteed to see 0 for y.

In this case, the final field spec guarantees that you will see 3 for x, as you point out. Think of the writer thread as having 4 instructions:

r1 = <create a new TestClass instance>
r1.x = 3;
r1.y = 4;
f = r1;

The reason you might not see 3 for x is if the compiler reordered this code:

r1 = <create a new TestClass instance>
f = r1;
r1.x = 3;
r1.y = 4;

The way the guarantee for final fields is usually implemented in practice is to ensure that the constructor finishes before any subsequent program actions take place. Imagine someone erected a big barrier between r1.y = 4 and f = r1. So, in practice, if you have any final fields for an object, you are likely to get visibility for all of them.

Now, in theory, someone could write a compiler that isn't implemented that way. In fact, many people have often talked about testing code by writing the most malicious compiler possible. This is particularly common among the C++ people, who have lots and lots of undefined corners of their language that can lead to terrible bugs.

Jeremy Manson
  • 201
  • 2
  • 2
7

From Java 5.0, you are guarenteed that all threads will see the final state set by the constructor.

If you want to see this fail, you could try an older JVM like 1.3.

I wouldn't print out every test, I would only print out the failures. You could get one failure in a million but miss it. But if you only print failures, they should be easy to spot.

A simpler way to see this fail is to add to the writer.

f.y = 5;

and test for

int y = TestClass.f.y; // could see 0, 4 or 5
if (y != 5)
    System.out.println("y = " + y);
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • I am guaranteed to see the state of final fields set by the constructor, but not non-final fields right? So, in my example, I should always see 3 for x, but I'm not guaranteed to see 4 for y. But for some reason, I ALWAYS see 4 for y, also. – sma Feb 21 '11 at 14:37
  • 2
    Java 5.0+ guarentees you will always see y = 4 in another thread unless you start the thread in the constructor. – Peter Lawrey Feb 21 '11 at 14:44
  • Am I just reading this wrong then? Or is this just not updated for the semantics that Java 5.0 introduced? http://java.sun.com/docs/books/jls/third_edition/html/memory.html#17.5 – sma Feb 21 '11 at 14:59
  • 2
    Reading the specification, I believe you are right that it appears to only guarentee final fields, however individual implementation are free to guarentee non-final fields as well. AFAIK Oracle's Java 5.0+ JVM ensures that all fields are visible to all threads as soon as the constructor completes. – Peter Lawrey Feb 21 '11 at 16:29
  • The Java has two classes; StrictMath and Math. They do the same things expect StrictMath follows the IEEE standards strictly, however Math might not. In Oracle JVM, the most Math methods just call the StrictMath methods or do the same thing. (there are a small number of exceptions, such as copySign() ) – Peter Lawrey Feb 21 '11 at 16:35
  • 1
    Understood. I'll have to take your word for it on that one because I was unable to find this documented anywhere regarding HotSpot. However, even if that is the case, there is no guarantee in my code that the constructor has completed when the reader thread accesses TestClass.f. The reference is already guaranteed to be written thanks to the null check, but there is no guarantee the constructor is finished or the fields have been defaulted. – sma Feb 21 '11 at 19:01
  • 2
    OK, I am incorrect in my statement above. Turns out the reference to f is set AFTER the constructor completes. I was able to get this to fail by unsafely publishing a reference in the constructor such as: f = this. In this instance, I saw failures on both variables, which is consistent with the semantics on unsafe publishing. However, I am still unable to get the safe publishing guarantee to fail on variable y. – sma Feb 21 '11 at 19:22
  • 1
    *If you want to see this fail, you could try an older JVM like 1.3.* Probably you still need to run on ARM/Power architecture to achieve that. On a side note the answer is correct, so whoever disliked should have dropped a note. – bestsss Mar 10 '13 at 19:35
  • Could you tell, why could this code fail on older JVMs? Did JLS changed? – Mikhail Mar 11 '13 at 16:23
  • If you add f.y = 5; to the writer after constructor is invoked, then whats the aim of such test will be? In this case failures are predictable. – Mikhail Mar 11 '13 at 16:36
  • 1
    @Noofiz, JMM happened in 1.5 and that was along of the greatest movement towards memory models in many lanaguges (C#, C++ incl). It was backported to 1.4 afterward, hence you'd need 1.3 or so. – bestsss Mar 11 '13 at 17:47
  • -1. The code posted in the question will not fail in any JVM, or in any programming language for that matter. See my answer. – Tobia Mar 17 '13 at 11:16
  • @Tobia Your answer doesn't mention f.y AFAIK so I don't see hwo it is related. `f.y` is inlined in code by the JVM when it detects that a non-volatile variable is not altered by a thread. This can result in it not seeing a new value, ever. – Peter Lawrey Mar 17 '13 at 11:28
  • @PeterLawrey It doesn't matter whether f.y in inlined or not. Yes, the thread could fail to see a *new value* for f, but it will never see a value that has *not yet been assigned* to f. The assignment takes place after the evaluation of the R-value, which in this case calls a method that initializes both x and y. – Tobia Mar 17 '13 at 11:55
  • @Tobia In Java, it can fail to see a value it has been initialised to (if non-final) This means another thread could see 0, or 4 or possibly 5 if `f.y = 5` if called. It is possible another thread could see 5 and later 4 as the order of assignment is not guaranteed. – Peter Lawrey Mar 17 '13 at 12:17
  • "the order of assignment is not guaranteed" do you have any citation for this? – Tobia Mar 17 '13 at 14:49
  • Moreover, are you really telling me that if one piece of Java code creates a new object `o`, whose constructor sets `this.y = 4`, ***and then*** the code assigns `o` to whatever static field `f`, you maintain that other threads scrambling to read `f` could catch a glimpse of a version of `o` which was assigned to `f` before its constructor ran? Do you realize such a heresy goes against not only common sense, but the definition of "constructor" and "assignment" in the Java Language Specification? No wonder the OP cannot reproduce such a problem, it would mean his JVM was seriously broken! – Tobia Mar 17 '13 at 14:57
  • 1
    @Tobia You seem to significantly underestimate what reorderings are allowed by the language when several threads are involved. – assylias Mar 17 '13 at 18:23
  • Not only is there code re-ordering, but also write re-ordering esp between caches for different threads or lines of a cache for the same core. e.g. if x and y are on different cache lines they are pretty much independent and the delay between visible writes can be large. – Peter Lawrey Mar 17 '13 at 21:04
  • @PeterLawrey I would have thought large rarely exceeds a few microseconds. – assylias Mar 17 '13 at 22:46
  • I have tried hard to prove this, but no luck. [here](https://stackoverflow.com/a/59385508/1059372) is my struggle with that. – Eugene Dec 18 '19 at 12:36
5

I'd like to see a test which fails or an explanation why it's not possible with current JVMs.

Multithreading and Testing

You can't prove that a multithreaded application is broken (or not) by testing for several reasons:

  • the problem might only appear once every x hours of running, x being so high that it is unlikely that you see it in a short test
  • the problem might only appear with some combinations of JVM / processor architectures

In your case, to make the test break (i.e. to observe y == 0) would require the program to see a partially constructed object where some fields have been properly constructed and some not. This typically does not happen on x86 / hotspot.

How to determine if a multithreaded code is broken?

The only way to prove that the code is valid or broken is to apply the JLS rules to it and see what the outcome is. With data race publishing (no synchronization around the publication of the object or of y), the JLS provides no guarantee that y will be seen as 4 (it could be seen with its default value of 0).

Can that code really break?

In practice, some JVMs will be better at making the test fail. For example some compilers (cf "A test case showing that it doesn't work" in this article) could transform TestClass.f = new TestClass(); into something like (because it is published via a data race):

(1) allocate memory
(2) write fields default values (x = 0; y = 0) //always first
(3) write final fields final values (x = 3)    //must happen before publication
(4) publish object                             //TestClass.f = new TestClass();
(5) write non final fields (y = 4)             //has been reodered after (4)

The JLS mandates that (2) and (3) happen before the object publication (4). However, due to the data race, no guarantee is given for (5) - it would actually be a legal execution if a thread never observed that write operation. With the proper thread interleaving, it is therefore conceivable that if reader runs between 4 and 5, you will get the desired output.

I don't have a symantec JIT at hand so can't prove it experimentally :-)

assylias
  • 321,522
  • 82
  • 660
  • 783
  • "why it's not possible with current JVMs" - the question is not about how bad JVMs were many years ago? It's about now days. – Mikhail Mar 14 '13 at 09:19
  • 1
    @Noofiz I think you don't get the point: **this is a legal reordering under the current JLS (v7)** - the fact that the reordering is unlikely to happen on a current x86/hotspot combination is irrelevant. – assylias Mar 14 '13 at 09:51
  • On what JVM and what architecture it might happen then? Please, provide an article or something. – Mikhail Mar 14 '13 at 10:02
  • 1
    @Noofiz I don't know but it does not matter: it could happen according to the specifications of the language. – assylias Mar 14 '13 at 10:07
  • 1
    *write fields default values (x = 0; y = 4) //always first* -- both should be zero as default values - if 4 is written your example fails; I seriously do not see a point in discussing such stuff on stackoverflow, the audience is really far off the mark and in my opinion most of the people just use as copy source to write their own code. – bestsss Mar 14 '13 at 11:07
  • 1
    another note: (4) can be satisfied only if publishing w/ race, if using volatile/cas/lazySet (4) has to come last. Edit: Probably [http://altair.cs.oswego.edu/pipermail/concurrency-interest/2012-August/009801.html](the best) advice I have seen on writing user-space concurrent code. – bestsss Mar 14 '13 at 11:41
  • Correct link: http://altair.cs.oswego.edu/pipermail/concurrency-interest/2012-August/009801.html – assylias Mar 14 '13 at 11:58
  • Why should the right-hand code of an assignment expression be reordered after the actual assignment? That does not make any sense and would break any program. Not to mention it violates the very definition of assignment, according to the Java language specification. See my answer. – Tobia Mar 17 '13 at 11:22
  • 1
    @Tobia That's not me saying, the reodering I took has been explained by the people who wrote the JLS Chapter 17. [This post](http://jeremymanson.blogspot.co.uk/2007/03/safe-construction-and-jmm.html) details a similar example and has been written by one of the major contributor to the new memory model introduced with Java 5. – assylias Mar 17 '13 at 18:20
  • in current, jdk-13, this is [impossible to prove, btw](https://stackoverflow.com/a/59385508/1059372) – Eugene Dec 18 '19 at 12:36
3

Here is an example of default values of non final values being observed despite that the constructor sets them and doesn't leak this. This is based off my other question which is a bit more complicated. I keep seeing people say it can't happen on x86, but my example happens on x64 linux openjdk 6...

Community
  • 1
  • 1
Dog
  • 7,707
  • 8
  • 40
  • 74
3

This is a good question with a complicated answer. I've split it in pieces for an easier read.

People have said here enough times that under the strict rules of JLS - you should be able to see the desired behavior. But compilers (I mean C1 and C2), while they have to respect the JLS, they can make optimizations. And I will get to this later.

Let's take the first, easy scenario, where there are two non-final variables and see if we can publish an in-correct object. For this test, I am using a specialized tool that was tailored for this kind of tests exactly. Here is a test using it:

@Outcome(id = "0, 2", expect = Expect.ACCEPTABLE_INTERESTING, desc = "not correctly published")
@Outcome(id = "1, 0", expect = Expect.ACCEPTABLE_INTERESTING, desc = "not correctly published")
@Outcome(id = "1, 2", expect = Expect.ACCEPTABLE, desc = "published OK")
@Outcome(id = "0, 0", expect = Expect.ACCEPTABLE, desc = "II_Result default values for int, not interesting")
@Outcome(id = "-1, -1", expect = Expect.ACCEPTABLE, desc = "actor2 acted before actor1, this is OK")
@State
@JCStressTest
public class FinalTest {

    int x = 1;
    Holder h;

    @Actor
    public void actor1() {
        h = new Holder(x, x + 1);
    }

    @Actor
    public void actor2(II_Result result) {
        Holder local = h;
        // the other actor did it's job
        if (local != null) {
            // if correctly published, we can only see {1, 2} 
            result.r1 = local.left;
            result.r2 = local.right;
        } else {
            // this is the case to "ignore" default values that are
            // stored in II_Result object
            result.r1 = -1;
            result.r2 = -1;
        }
    }

    public static class Holder {

        // non-final
        int left, right;

        public Holder(int left, int right) {
            this.left = left;
            this.right = right;
        }
    }
}

You do not have to understand the code too much; though the very minimal explanations is this: there are two Actors that mutate some shared data and those results are registered. @Outcome annotations control those registered results and set certain expectations (under the hood things are far more interesting and verbose). Just bare in mind, this is a very sharp and specialized tool; you can't really do the same thing with two threads running.

Now, if I run this, the result in these two:

 @Outcome(id = "0, 2", expect = Expect.ACCEPTABLE_INTERESTING....)
 @Outcome(id = "1, 0", expect = Expect.ACCEPTABLE_INTERESTING....)

will be observed (meaning there was an unsafe publication of the Object, that the other Actor/Thread has actually see).

Specifically these are observed in the so-called TC2 suite of tests, and these are actually run like this:

java... -XX:-TieredCompilation 
        -XX:+UnlockDiagnosticVMOptions 
        -XX:+StressLCM 
        -XX:+StressGCM

I will not dive too much of what these do, but here is what StressLCM and StressGCM does and, of course, what TieredCompilation flag does.

The entire point of the test is that:

This code proves that two non-final variables set in the constructor are incorrectly published and that is run on x86.


The sane thing to do now, since there is a specialized tool in place, change a single field to final and see it break. As such, change this and run again, we should observe the failure:

public static class Holder {

    // this is the change
    final int right;
    int left;

    public Holder(int left, int right) {
        this.left = left;
        this.right = right;
    }
}

But if we run it again, the failure is not going to be there. i.e. none of the two @Outcome that we have talked above are going to be part of the output. How come?

It turns out that when you write even to a single final variable, the JVM (specifically C1) will do the correct thing, all the time. Even for a single field, as such this is impossible to demonstrate. At least at the moment.


In theory you could throw Shenandoah into this and it's interesting flag : ShenandoahOptimizeInstanceFinals (not going to dive into it). I have tried running previous example with:

 -XX:+UnlockExperimentalVMOptions  
 -XX:+UseShenandoahGC  
 -XX:+ShenandoahOptimizeInstanceFinals  
 -XX:-TieredCompilation  
 -XX:+UnlockDiagnosticVMOptions  
 -XX:+StressLCM  
 -XX:+StressGCM 

but this does not work as I hoped it will. What is far worse for my arguments of even trying this, is that these flags are going to be removed in jdk-14.

Bottom-line: At the moment there is no way to break this.

Eugene
  • 117,005
  • 15
  • 201
  • 306
-1

What about you modified the constructor to do this:

public TestClass() {
 Thread.sleep(300);
   x = 3;
   y = 4;
}

I am not an expert on JLF finals and initializers, but common sense tells me this should delay setting x long enough for writers to register another value?

Jakub Zaverka
  • 8,816
  • 3
  • 32
  • 48
  • *What about you modified the constructor to do this* and nothing will happen (besides needs to throw the InterruperdException) - simple `f` will be null... – bestsss Mar 14 '13 at 10:17
  • @bestsss I had a feeling that this would be too simple to be correct – Jakub Zaverka Mar 14 '13 at 10:24
  • @bestsss: There is no such guarantee when the read is from another thread. It is legal to assign to `f` before running the constructor if the *same thread* cannot notice a difference (and [some JITs do this](http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html)). I'm not sure whether the compiler is clever enough to notice that `f` is not read in `Thread.sleep()` (a busy-wait might work better), but it requires the JIT to make this optimization, and some of them won't due to broken code. – tc. Jul 14 '13 at 14:37
-2

What if one changes the scenario into

public class TestClass {

    final int x;
    static TestClass f;

    public TestClass() {
        x = 3;
    }

    int y = 4;

    // etc...

}

?

Alexander Vasiljev
  • 1,974
  • 3
  • 19
  • 27
-3

Better understanding of why this test does not fail can come from understanding of what actually happens when constructor is invoked. Java is a stack-based language. TestClass.f = new TestClass(); consists of four action. First new instruction is called, its like malloc in C/C++, it allocates memory and places a reference to it on the top of the stack. Then reference is duplicated for invoking a constructor. Constructor in fact is like any other instance method, its invoked with the duplicated reference. Only after that reference is stored in the method frame or in the instance field and becomes accessible from anywhere else. Before the last step reference to the object is present only on the top of creating thread's stack and no body else can see it. In fact there is no difference what kind of field you are working with, both will be initialized if TestClass.f != null. You can read x and y fields from different objects, but this will not result in y = 0. For more information you should see JVM Specification and Stack-oriented programming language articles.

UPD: One important thing I forgot to mention. By java memory there is no way to see partially initialized object. If you do not do self publications inside constructor, sure.

JLS:

An object is considered to be completely initialized when its constructor finishes. A thread that can only see a reference to an object after that object has been completely initialized is guaranteed to see the correctly initialized values for that object's final fields.

JLS:

There is a happens-before edge from the end of a constructor of an object to the start of a finalizer for that object.

Broader explanation of this point of view:

It turns out that the end of an object's constructor happens-before the execution of its finalize method. In practice, what this means is that any writes that occur in the constructor must be finished and visible to any reads of the same variable in the finalizer, just as if those variables were volatile.

UPD: That was the theory, let's turn to practice.

Consider the following code, with simple non-final variables:

public class Test {

    int myVariable1;
    int myVariable2;

    Test() {
        myVariable1 = 32;
        myVariable2 = 64;
    }

    public static void main(String args[]) throws Exception {
        Test t = new Test();
        System.out.println(t.myVariable1 + t.myVariable2);
    }
}

The following command displays machine instructions generated by java, how to use it you can find in a wiki:

java.exe -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -Xcomp -XX:PrintAssemblyOptions=hsdis-print-bytes -XX:CompileCommand=print,*Test.main Test

It's output:

...
0x0263885d: movl   $0x20,0x8(%eax)    ;...c7400820 000000
                                    ;*putfield myVariable1
                                    ; - Test::<init>@7 (line 12)
                                    ; - Test::main@4 (line 17)
0x02638864: movl   $0x40,0xc(%eax)    ;...c7400c40 000000
                                    ;*putfield myVariable2
                                    ; - Test::<init>@13 (line 13)
                                    ; - Test::main@4 (line 17)
0x0263886b: nopl   0x0(%eax,%eax,1)   ;...0f1f4400 00
...

Field assignments are followed by NOPL instruction, one of it's purposes is to prevent instruction reordering.

Why does this happen? According to specification finalization happens after constructor returns. So GC thread cant see a partially initialized object. On a CPU level GC thread is not distinguished from any other thread. If such guaranties are provided to GC, than they are provided to any other thread. This is the most obvious solution to such restriction.

Results:

1) Constructor is not synchronized, synchronization is done by other instructions.

2) Assignment to object's reference cant happen before constructor returns.

Community
  • 1
  • 1
Mikhail
  • 4,175
  • 15
  • 31
  • 3
    The fact java byte code is stack-oriented has nothing in common with the question. Bytecode is compiled in optimized machine's native code, which in turn has no sign of stack origin. – Alexei Kaigorodov Mar 11 '13 at 17:15
  • 1
    To be short: the reference to the object becomes accessible only after constructor returns. If this is true, current test will not fail. Or JIT may change this order? – Mikhail Mar 11 '13 at 17:35
  • 1
    Thank you for the answer! "A thread that can only see a reference to an object after that object has been completely initialized is guaranteed to see the correctly initialized values for that object's **final** fields." If I'm right it says nothing about non-final fields. – palacsint Mar 12 '13 at 23:32
  • The only difference between final and non-final fields is that, non-final could be modified after constructor return. That's why no guaranties are providedfor them. The main thing is that while invocation constructor is synchronized on this reference. So returning from constructor happens-before any other actions on object. – Mikhail Mar 13 '13 at 07:05
  • 3
    @Noofiz "*By java memory model constructor invocation is synchronized. So there is no way to see partially initialized object.*" is plain wrong... – assylias Mar 14 '13 at 08:20
  • 1
    And "*The only difference between final and non-final fields is that, non-final could be modified after constructor return.*" is wrong too - final fields benefit from additional visibility guarantee. – assylias Mar 14 '13 at 08:23
  • How are thees guaranties connected to this question? Both fields are primitive types. JVM Specification rather clearly says that a partially initialized object can not be seen. If do not agree, please, provide a test on 7th, not on the first ones, as was mentioned in comments here JMM has changed significantly. – Mikhail Mar 14 '13 at 09:24
  • 2
    "*JVM Specification rather clearly says that a partially initialized object can not be seen*" => No it does not. The JLS guarantees that a final field will always be visible if the constructor does not let `this` escape, even the object is published via a data race. There is no such guarantee for non-final fields. – assylias Mar 14 '13 at 09:36
  • "No it does not" => Ok, provide a test on later JVMs, to prove it. – Mikhail Mar 14 '13 at 09:41
  • 1
    @Noofiz I'm talking about the specifications of the language, which are implemented in many different ways. The specification can't be "proven" with a test. – assylias Mar 14 '13 at 09:52
  • Ok, than specifications says "There is a happens-before edge from the end of a constructor of an object to the start of a finalizer for that object" - this means that finalizer can not see a partially initialized object. Is't it enough? – Mikhail Mar 14 '13 at 09:59
  • 2
    @Noofiz It only says what it says: an object can't be finalized before it is fully constructed - but finalization only happen when the object is garbage collected. That does not guarantee anything if you use the object after construction. – assylias Mar 14 '13 at 10:06
  • If seen of partially initialized object was possible to some thread, it was also possible to a GC thread. In this case GC is not distinguished from other threads. – Mikhail Mar 14 '13 at 10:10
  • 3
    Your link (to a very good blog) does not prove that "constructor invocation is synchronized" - only that when the `finalize()` method runs, any writes made by the constructor will be made visible. It does not say anything about reads that happen before finalization. Actually you will note that Jemery says: "*any writes that occur in the constructor must be finished and visible to any reads of the same variable in the finalizer, just **as if those variables were volatile**.*" - he implicitly refers to the fact that such a read would not be guaranteed to see the write outside the finalizer. – assylias Mar 15 '13 at 14:57
  • How variables could be made visible to GC thread without making them visible to other threads. If you explain me this, I admit that I'am wrong. By now I don't understand this moment. This looks illogical to me. – Mikhail Mar 15 '13 at 15:12
  • See for example: http://jeremymanson.blogspot.co.uk/2007/03/safe-construction-and-jmm.html – assylias Mar 15 '13 at 15:55
  • @assylias, this conclusions are based on only a part of theory, having nothig common with practice. – Mikhail Mar 19 '13 at 08:49
  • 2
    @Noofiz Yes it is based on theory - the fact that your JVM does not do it does not mean that the program is not broken. See [this other answer](http://stackoverflow.com/a/15517168/829571) (worth a welcome upvote!). – assylias Mar 20 '13 at 23:22
  • I would like to down vote, but then this question becomes invisible and all the good comments are lost. – user2864740 Nov 21 '13 at 07:05
-3

What's going on in this thread? Why should that code fail in the first place?

You launch 1000s of threads that will each do the following:

TestClass.f = new TestClass();

What that does, in order:

  1. evaluate TestClass.f to find out its memory location
  2. evaluate new TestClass(): this creates a new instance of TestClass, whose constructor will initialize both x and y
  3. assign the right-hand value to the left-hand memory location

An assignment is an atomic operation which is always performed after the right-hand value has been generated. Here is a citation from the Java language spec (see the first bulleted point) but it really applies to any sane language.

This means that while the TestClass() constructor is taking its time to do its job, and x and y could conceivably still be zero, the reference to the partially initialized TestClass object only lives in that thread's stack, or CPU registers, and has not been written to TestClass.f

Therefore TestClass.f will always contain:

  • either null, at the start of your program, before anything else is assigned to it,
  • or a fully initialized TestClass instance.
Tobia
  • 17,856
  • 6
  • 74
  • 93
  • 1
    This definitely make sense to me. But the TestClass of the original poster is actually copied verbatim from the [JLS](http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5), which do specify that the reader could see `y` as `0`. – Étienne Miret Mar 17 '13 at 17:14
  • 2
    You are misunderstanding the situation - as soon as a program become multithreaded, most of the JLS becomes inapplicable. This is well summarised in [Chapter 17.4](http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4-120): *The actions of each thread in isolation must behave as governed by the semantics of that thread, with the exception that the values seen by each read are determined by the memory model*. – assylias Mar 17 '13 at 18:17
  • 1
    In other words, everything you say is correct if there is only one thread, but if there is more than one, all the reads of shared variables in `reader` are governed by Chapter 17 and most of your post does not apply. – assylias Mar 17 '13 at 18:19