views:

746

answers:

6

Hi,

Say you are creating a java object like so:

SomeClass someObject = null;
someObject = new SomeClass();

At what point does the someObject become non-null? Is it before the SomeClass() constructor runs or after?

To clarify a little, say if another thread was to check if someObject was null while the SomeClass() constructor was halfway through completion, would it be null or non-null?

Also, what would be the difference if someObject was created like so:

SomeClass someObject = new SomeClass();

Would someObject ever be null?

+10  A: 

If another thread were to check the someObject variable "during" construction, I believe it may (due to quirks in the memory model) see a partially initialized object. The new (as of Java 5) memory model means that any final fields should be set to their values before the object becomes visible to other threads (so long as the reference to the newly created object doesn't escape from the constructor in any other way) but beyond that there aren't many guarantees.

Basically, don't share data without appropriate locking (or guarantees given by static inializers etc) :) Seriously, memory models are seriously tricky, as is lock-free programming in general. Try to avoid this becoming a possibility.

In logical terms the assignment happens after the constructor runs - so if you observe the variable from the same thread it will be null during the constructor call. However, as I say there are memory model oddities.

EDIT: For the purposes of double-checked locking, you can get away with this if your field is volatile and if you're using Java 5 or higher. Prior to Java 5 the memory model wasn't strong enough for this. You need to get the pattern exactly right though. See Effective Java, 2nd edition, item 71 for more details.

EDIT: Here's my reasoning for arguing against Aaron's inlining being visible in a single thread. Suppose we have:

public class FooHolder
{
    public static Foo f = null;

    public static void main(String[] args)
    {
        f = new Foo();
        System.out.println(f.fWasNull);
    }
}

// Make this nested if you like, I don't believe it affects the reasoning
public class Foo
{
    public boolean fWasNull;

    public Foo()
    {
        fWasNull = FooHolder.f == null;
    }
}

I believe this will always report true. From section 15.26.1:

Otherwise, three steps are required:

  • First, the left-hand operand is evaluated to produce a variable. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason; the right-hand operand is not evaluated and no assignment occurs.
  • Otherwise, the right-hand operand is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason and no assignment occurs.

Otherwise, the value of the right-hand operand is converted to the type of the left-hand variable, is subjected to value set conversion (§5.1.13) to the appropriate standard value set (not an extended-exponent value set), and the result of the conversion is stored into the variable.

Then from section 17.4.5:

Two actions can be ordered by a happens-before relationship. If one action happens-before another, then the first is visible to and ordered before the second.

If we have two actions x and y, we write hb(x, y) to indicate that x happens-before y.

  • If x and y are actions of the same thread and x comes before y in program order, then hb(x, y).
  • There is a happens-before edge from the end of a constructor of an object to the start of a finalizer (§12.6) for that object.
  • If an action x synchronizes-with a following action y, then we also have hb(x, y).
  • If hb(x, y) and hb(y, z), then hb(x, z).

It should be noted that the presence of a happens-before relationship between two actions does not necessarily imply that they have to take place in that order in an implementation. If the reordering produces results consistent with a legal execution, it is not illegal.

In other words, it's okay for weird stuff to happen even within a single thread but that mustn't be observable. In this case the difference would be observable, which is why I believe it would be illegal.

Jon Skeet
What kind of quirks (I don't know much Java)? I am curious to know what would allow something like this because you would think that since the constructor had not finished running that the assignment to the variable would not have taken place yet which would mean the variable would still be null.
Andrew Hare
Yeah, there's no "happens-before" there. There are some extra guarantees for final fields. In general, don't write code with races.
Tom Hawtin - tackline
Tom: Could you double-check what I've written here? I'm loathe to speak very definitively about this kind of thing...
Jon Skeet
@Andrew: It's more that the constructor could actually have finished, and the assignment itself be visible, but other fields written *during* the construction may not have been made visible to other threads yet.
Jon Skeet
-1 See my answer.
Aaron Digulla
See my comments to your answer. I don't believe your understanding of why double-checked locking was broken is quite right.
Jon Skeet
@Jon - Ah I can see why that might create problems.
Andrew Hare
The only caveat for final fields is that a reference to the object must not have escaped until after the constructor exits (it is okay if a subclass constructor subsequently leaks this - but you have to squint at the spec).
Tom Hawtin - tackline
@tackline: Thanks. I'll edit to make that clearer. Care to comment on Aaron's answer too?
Jon Skeet
@Jon The main reason I asked this question is to understand the Double Checked Locking Singleton anti-pattern. I assume that this is why it does not work (as another thread may get non-null but partially constructed object)??
Lehane
@Jon Source for last comment:http://javabeans.asia/2008/09/06/singleton_pattern_and_problem_with_double_checked_locking.html
Lehane
@Lehane: I've edited my answer to explain about DCL.
Jon Skeet
@Jon: Thanks for that.
Lehane
Edits fix my concerns.
Aaron Digulla
+2  A: 

someObject will be a null pointer right up until it is assigned a pointer value from the constructor of the type. Since assignment is from right to left it is possible for another thread to check someObject while the constructor is still running. This would be prior to the assignment of the pointer to the variable so someObject would still be null.

Andrew Hare
-1 this is not true in Java! See my answer.
Aaron Digulla
Ah - very interesting stuff - thanks!
Andrew Hare
A: 

For your first example: someObject becomes non-null AFTER the constructor has completed. If you would check from another thread, someObject would become non-null after the constructor has finished. Beware, you should never access unsynchronized objects from different threads, so your example should not be implemented that way in real-world code.

For the second example, someObject would never be null as it is constructed AFTER SomeClass itself is constructed and someObject is created&initialized with the newly created object. Same here for threads: don't access this variable from different threads without synchronisation!

Kosi2801
-1 Not true for Java.
Aaron Digulla
+7  A: 

someObject will become non-null at some point during the construction. Typically, there are two cases:

  1. The optimizer has inlined the constructor
  2. The constructor is not inlined.

In the first case, the VM will execute this code (pseudocode):

someObject = malloc(SomeClass.size);
someObject.field = ...
....

So in this case, someObject is not null and it points to memory that is not 100% initialized, namely not all of the constructor code has been run! This is why double-checked locking doesn't work.

In the second case, the code from the constructor will run, a reference will be passed back (just like in a normal method call) and someObject will be set to the value of the refernce after all and every init code has run.

The problem is that there is no way to tell java not to assign someObject early. For example, you could try:

SomeClass tmp = new SomeClass();
someObject = tmp;

But since tmp is not used, the optimizer is allowed to ignore it, so it would produce the same code as above.

So this behavior is to allow the optimizer to produce faster code but it can bite you nastily when writing multi-threaded code. In single threaded code, this is usually not an issue since no code is executed until the constructor finishes.

[EDIT] Here is a good article which explains what's happening: http://www.ibm.com/developerworks/java/library/j-dcl.html

PS: The book "Effective Java, Second Edition" by Joshua Bloch contains a solution for Java 5 and up:

private volatile SomeClass field;
public SomeClass getField () {
    SomeClass result = field;
    if (result == null) { // First check, no locking
        synchronized(this) {
            result = field;
            if (result == null) { // second check with locking
                field = result = new SomeClass ();
            }
        }
    }
    return result;
}

Looks weird but should work on every Java VM. Note that every bit is important; if you omit the double assign, you'll either get bad performance or partially initialized objects. For a complete explanation, buy the book.

Aaron Digulla
I don't believe it's really inlining that's the problem, as then the effect would be visible within the same thread (which I don't believe it can be). Instead, it's the vagaries of the memory model in terms of which writes are published to other threads first.
Jon Skeet
In particular, I don't *believe* that the thread doing the construction can see someObject as non-null during the constructor, contrary to your pseudocode. If you think that it *can* see a non-null value, please give sample code :)
Jon Skeet
As I said, in single threaded code, the inlining usually doesn't cause any problems. But when you have more than one thread, the thread which didn't start the constructor can see a partially constructed object.
Aaron Digulla
I think you're just shifting the problem. What's the value of someObject in the VM before it gets mallocs value assigned?Furthermore I suspect that the optimizer realizes the null value will be never used and optimizes the first example to the second form which leaves someObject null for some time.
Kosi2801
My argument is that you should not rely on this even though is works in single-threaded code. As soon as you need to multi-thread it, you will see really odd bugs. So never depend write code which depends on the time at which the refrence will become non-null.
Aaron Digulla
@Aaron: In single threaded code, if your pseudo-code is correct, you'd be able to see a non-null value for someObject within the constructor. I don't believe this is possible - I believe your pseudocode is incorrect. Instead, the memory model is the issue here.
Jon Skeet
Kosi: The problem is that someObject becomes != null at some time but you can't depend on the fact that you can use the object at that time.
Aaron Digulla
@Aaron: You *should* be able to rely on it during single-threaded operation though, because I believe that's what the spec states. The assignment logically happens after the construction, so within a single thread this intra-thread order should be preserved. With your inlining claim, it wouldn't be.
Jon Skeet
It must never be a problem in single-threaded code. The compiler can rearrange it (it's usually a software not a hardware problem), but this must not be visible to a single thread.
Tom Hawtin - tackline
@Jon: See Listing 6 of the IBM link. It shows what byte code is generated and there you can see that someObject is assigned first and then the constructor runs.
Aaron Digulla
If you make the (non-local(!)) variable volatile, the it will behave as you might expect with the object appearing to finish construction before assignment as viewed by all threads.
Tom Hawtin - tackline
@Aaron: Interesting - I wonder what the JIT would do if the constructor referred to "instance". If it still emitted the same code, I believe it would be violating the specs. Which part of my answer do you believe is actually wrong, btw?
Jon Skeet
(The constructor also couldn't make any other method calls which *might* refer to that variable, for the same reason.)
Jon Skeet
@Tom: see my edit.
Aaron Digulla
@Jon: If you put the variable someObject into a field and make SomeClass a nested, I think you might see that someObject is != null in the constructor if the JIT has inlined the call. Making SomeClass final should do the trick.
Aaron Digulla
@Aaron: Thanks, understanding the DCL problem is why I asked the question in the first place.
Lehane
@Aaron: Please try to demonstrate that. I really don't think it should happen - I believe it would violate the first bullet point of section 17.4.5 of the spec.
Jon Skeet
You can absolutely rely on it in a single thread, anything otherwise would violate in-thread as-if-serial semantics. Whether the method is inlined or not simply adds more reordering to the code, without violating this principle.
Robin
@Jon: A simple example doesn't show this; I would need to write a program that is complex enough to invoke the JIT for the class constructor.
Aaron Digulla
@Jon: But it's perfectly possible that the Java gurus added a rule to prevent this issue in the single-threaded case. I'm mostly worried with the multi-threaded one.
Aaron Digulla
@Aaron: We've already agreed on the multi-threaded part, and my answer (which you downvoted) didn't claim otherwise. I'm challenging your claim that it's possible in a single thread case - I've edited my answer to show my reasoning. Please comment with which bit of my answer you disagree with.
Jon Skeet
@Robin: My guess is that the optimizer somehow figures out when it is safe to reorder the assignment and when not. see the answer by omerkudat which also has to work in every case.
Aaron Digulla
@Jon: My subconsciousness wails ;) No idea why. I'm really surprised that this rare case works. I mean: When is the constructor really going to look for a variable it's instance is being assigned to?? ;)
Aaron Digulla
going to be assigned to, even :)
Aaron Digulla
It's not so much a case of that being a rare case as "common sense should prevail for a single thread". Optimizations should make a mockery of common sense for a single thread, even if they can mess with your brain for multi-threaded operations.
Jon Skeet
@Aaron: Yes, but the reordering and inlining are not the same thing. This is why inlining is not relevant to the problem.
Robin
@Robin: It's relevant in that it's allowed *because* of the memory model, so long as the JIT can ensure that single-thread operation is happening. However, I believe this could be a problem *without* inlining due to memory cache behaviour. The JIT doesn't need to make the writes visible in order.
Jon Skeet
A: 

From another thread, your object will still look null until the constructor has finished executing. This is why if the construction is terminated by an exception, the reference will remain null.

Object o = null;
try {
    o = new CtorTest();
} catch (Exception e) {
    assert(o == null); // i will be null
}

where

class CtorTest {
    public CtorTest() {
        throw new RuntimeException("Ctor exception.");
    }
}

Make sure to synchronize on another object, not the one being constructed.

omerkudat
This is interesting; o can be != null while the constructor runs but I guess the optimizer will assign the reference to a temporary variable if you have a try-catch.
Aaron Digulla
A: 

Here's some test code which shows that the object is null until the constructor has finished running:

public class Test {

  private static SlowlyConstructed slowlyConstructed = null;

  public static void main(String[] args) {
    Thread constructor = new Thread() {
      public void run() {
        Test.slowlyConstructed = new SlowlyConstructed();
      }
    };
    Thread checker = new Thread() {
      public void run() {
        for(int i = 0; i < 10; i++) {
          System.out.println(Test.slowlyConstructed);
          try { Thread.sleep(1000); }
          catch(Exception e) {}
        }
      }
    };

    checker.start();
    constructor.start();
  }

  private static class SlowlyConstructed {
    public String s1 = "s1 is unset";
    public String s2 = "s2 is unset";

    public SlowlyConstructed() {
      System.out.println("Slow constructor has started");
      s1 = "s1 is set";
      try { Thread.sleep(5000); }
      catch (Exception e) {}
      s2 = "s2 is set";
      System.out.println("Slow constructor has finished");
    }

    public String toString() {
      return s1 + ", " + s2;
    }
  }
}

Output:

null
Slow constructor has started
null
null
null
null
null
Slow constructor has finished
s1 is set, s2 is set
s1 is set, s2 is set
s1 is set, s2 is set
s1 is set, s2 is set
Kip
You cannot perform proofs in that way.
Tom Hawtin - tackline
+1 for "this is not a proof".
Jon Skeet
what is wrong with this? he asked when it happens, I wrote some code which shows when it happens.
Kip
Due to the nature of threading and different optimizations of different VM's you cannot prove or disprove the question via a simple code example.
Robin
ok fine i changed the word "proves" to "shows"
Kip