Pre-knowledge: the life cycle of a class
Scene design and speculation
-
Situation:
Instantiating B in Class A initialization
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:
Class Load Endpoint (text: Loaded Main2 from file)
Class initialization start/end point (text: Class A2 init)
Instance initialization start/end point (text: Instance A2 init)
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
Class's circular initialization does not cause deadlocks
The beginning of the five stages is orderly, but the end is not necessarily.
Stage is not exclusive/critical
-
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