Search code examples
javajvm

How JVM decides in which order to initialize classes (invoke <clinit>)?


From my knowledge, when JVM is loading and generating code for A's clinit, if it encounters an unresolved class B, the compiler will first emit a method call to JVM internal to initialize B, then compile the method call/field use of B normally. So at run-time, B is ensured to be initialized before its field or method is used.

Please correct me if I made any mistake in the above.

Then I don't understand how JVM deal with a situation like below.

public class A {
  public static A a = new A(B.b);
  public A(B b) {
    a = null;
  }

  public static void main(String[] args) {
    System.out.println(a == null);
  }
}

public class B {
  public static B b = new B(A.a);
  public B(A a) {}
}

Could someone explain how these two classes get initialized since they both need the other part to be initialized first? And why the main method in A returns false, since the static field a should be set null in A's constructor?


Solution

  • The second question is easy; the order of operations for the static initialization of A is: B.b is evaluated, new A(B.b) is evaluated (setting a to null), a is assigned new A(B.b). If any other instances of A were created, then a would be null. I think this should be easy to see without detailed knowledge about class initialization.

    The first question is a bit more involved. The relevant chapter in the JLS is chapter 12 on execution. The basic idea is this: first a class is loaded, then it is linked, then it is initialized. As part of the linking, the class is prepared; this is when its static fields are created and set to their default values, which is null for references (JLS 12.3.2) -- this happens before static initalization.

    So things happen in this order: A is loaded and linked, so a is initialized to null. Then the static initializer runs, so new A(B.b) needs to be evaluated. Now B is loaded and linked, and its static initializer runs, and new B(A.a) is evaluated, and at this point a is null and nothing special happens. Then A's constructor runs and the newly created object is assigned to a.