Does cyclic initialization of [Java] classes cause deadlocks?

Posted by cidesign on Sun, 16 Jun 2019 20:52:34 +0200

  • Pre-knowledge: the life cycle of a class

Scene design and speculation

  • Situation:

    1. Instantiating B in Class A initialization

    2. Instantiating A in Class B initialization

  • Class Design

    • Type A:

      • Static variable a=new B(); static variable A1 = 1 (then assigned to 2 in the static initialization block);

      • Instance variable A2 = 11 (then initialize the block with a value of 12);

      • Constructor;

    • Class B:

      • Static variable b=new A(); static variable B1 = 3 (then assigned to 4 in the static initialization block);

      • Instance variable B2 = 21 (then initialization block assignment is 22);

      • Constructor;

  • Guess the result of execution: Class A initialization needs B instantiation because of class instantiation after class initialization. Class B instantiation needs A initialization, which results in cyclic dependency. The final result is deadlock.

  • Point position:

    1. Class Load Endpoint (text: Loaded Main2 from file)

    2. Class initialization start/end point (text: Class A2 init)

    3. Instance initialization start/end point (text: Instance A2 init)

    4. Constructor endpoint (text: Instance A2 new)

Scenario code

class A2 {
    static {
        System.out.println("Class A2 init start");
    }

    static B2 a = new B2();
    static int a1 = 1;

    {
        System.out.println("Instance A2 init start. \ta=" + a + " \ta1=" + a1 + " \ta.b2=" + (a == null ? "NPE" : a.b2) + " \tb=" + B2.b + " \tb1=" + B2.b1 + " \tb.a2=" + (B2.b == null ? "NPE" : B2.b.a2));
    }

    public int a2 = 11;

    static {
        a1 = 2;
        System.out.println("Class A2 init end. \ta=" + a + " \ta1=" + a1 + " \ta.b2=" + (a == null ? "NPE" : a.b2) + " \tb=" + B2.b + " \tb1=" + B2.b1 + " \tb.a2=" + (B2.b == null ? "NPE" : B2.b.a2));
    }

    {
        a2 = 12;
        System.out.println("Instance A2 init end. \ta=" + a + " \ta1=" + a1 + " \ta.b2=" + (a == null ? "NPE" : a.b2) + " \tb=" + B2.b + " \tb1=" + B2.b1 + " \tb.a2=" + (B2.b == null ? "NPE" : B2.b.a2));
    }

    public A2() {
        System.out.println("Instance A2 new. \ta=" + a + " \ta1=" + a1 + " \ta.b2=" + (a == null ? "NPE" : a.b2) + " \tb=" + B2.b + " \tb1=" + B2.b1 + " \tb.a2=" + (B2.b == null ? "NPE" : B2.b.a2));
    }
}

class B2 {
    static {
        System.out.println("Class B2 init start");
    }

    static A2 b = new A2();
    static int b1 = 3;

    {
        System.out.println("Instance B2 init start. \tb=" + b + " \tb1=" + b1 + " \tb.a2=" + (b == null ? "NPE" : b.a2) + " \ta=" + A2.a + " \ta1=" + A2.a1 + " \ta.b2=" + (A2.a == null ? "NPE" : A2.a.b2));
    }

    public int b2 = 21;

    static {
        b1 = 4;
        System.out.println("Class B2 init end. \tb=" + b + " \tb1=" + b1 + " \tb.a2=" + (b == null ? "NPE" : b.a2) + " \ta=" + A2.a + " \ta1=" + A2.a1 + " \ta.b2=" + (A2.a == null ? "NPE" : A2.a.b2));
    }

    {
        b2 = 22;
        System.out.println("Instance B2 init end. \tb=" + b + " \tb1=" + b1 + " \tb.a2=" + (b == null ? "NPE" : b.a2) + " \ta=" + A2.a + " \ta1=" + A2.a1 + " \ta.b2=" + (A2.a == null ? "NPE" : A2.a.b2));
    }

    public B2() {
        System.out.println("Instance B2 new. \tb=" + b + " \tb1=" + b1 + " \tb.a2=" + (b == null ? "NPE" : b.a2) + " \ta=" + A2.a + " \ta1=" + A2.a1 + " \ta.b2=" + (A2.a == null ? "NPE" : A2.a.b2));
    }
}

class Main2 {
    static public void main(String... args) {
        System.out.println("A2 a=" + A2.a);
        System.out.println("A2 a1=" + A2.a1);
        System.out.println("A2 a2=" + B2.b.a2);
        System.out.println("B2 b=" + B2.b);
        System.out.println("B2 b1=" + B2.b1);
        System.out.println("B2 b2=" + A2.a.b2);
    }
}

Analysis of implementation results

Program output results:

1. [Loaded Main2 from file:/Users/jiadongy/JVM_Learning_Sample/out/production/JVM_Learning_Sample/]
2. [Loaded A2 from file:/Users/jiadongy/JVM_Learning_Sample/out/production/JVM_Learning_Sample/]
3. Class A2 init start
4. [Loaded B2 from file:/Users/jiadongy/JVM_Learning_Sample/out/production/JVM_Learning_Sample/]
5. Class B2 init start
6. Instance A2 init start. a=null a1=0 a.b2=NPE b=null b1=0 b.a2=NPE
7. Instance A2 init end.      a=null a1=0 a.b2=NPE b=null b1=0 b.a2=NPE
8. Instance A2 new.         a=null a1=0 a.b2=NPE b=null b1=0 b.a2=NPE
9. Class B2 init end.              b=A2@61bbe9ba b1=4 b.a2=12 a=null a1=0 a.b2=NPE
10. Instance B2 init start.       b=A2@61bbe9ba b1=4 b.a2=12 a=null a1=0 a.b2=NPE
11. Instance B2 init end.          b=A2@61bbe9ba b1=4 b.a2=12 a=null a1=0 a.b2=NPE
12. Instance B2 new.             b=A2@61bbe9ba b1=4 b.a2=12 a=null a1=0 a.b2=NPE
13. Class A2 init end.      a=B2@610455d6 a1=2 a.b2=22 b=A2@61bbe9ba b1=4 b.a2=12
14. A2 a=B2@610455d6
15. A2 a1=2
16. A2 a2=12
17. B2 b=A2@61bbe9ba
18. B2 b1=4
19. B2 b2=22

Transform it into the following table to describe more clearly the process of each stage of A/B:

A B
Class A Loading Completed
Class A initialization - start
Class B loading completed
Class B Initialization - Start
Class A Instance Initialization - Start
Class A instance initialization-termination
Class A Instance Constructor Execution Completion
Class B Initialization-End
Class B Instance Initialization-Start
Class B instance initialization-termination
Class B Instance Constructor Execution Completion
Class A Initialization-End

You can see that in the process of class A initialization, class A is instantiated (and the stage is normally over), that is to say, the initialization stage of class is not atomic/exclusive.
For example, in this case, the class A instantiation phase ends earlier than the class initialization phase. When class A instantiation is completed, the static variables of class A have not been initialized.

This situation has been described in Reference.1:

_ Loading/validation/preparation/initialization and uninstallation are five stages in a definite order. The loading process of classes must start in a step-by-step manner. Note that the author here writes about a step-by-step "start" rather than a step-by-step "proceed" or "finish", emphasizing this point because these stages are all carried out in a cross-mixed way, usually in one stage. Call/activate another phase in the process

                                                          Deep Understanding of Java Virtual Machine > P210

summary

  1. Class's circular initialization does not cause deadlocks

  2. The beginning of the five stages is orderly, but the end is not necessarily.

  3. Stage is not exclusive/critical

  4. Loop initialization can cause unexpected situations, try to avoid

    • eg. Class modifies variables of another class during initialization, resulting in an unexpected initial value for another class

Reference

  1. Deep Understanding of Java Virtual Machines

Topics: Java