java class loading of initialization process (boundary questions)

Class or interface initialization process is executed initialization method thereof <clinit>. This method is generated by the compiler at compile time to the class file that contains class static field and the static assignment instruction block of statements (static {}) in the two-part code instructions in the same order and in the source code.

Under the following circumstances, it will trigger type (denoted by C) initialization:

  • new (create an object) , GetStatic (Get Class field) , putstatic (to the class field assignment) , or invokestatic (calling a class method) instruction is executed, create an instance of C, Get / Set C static fields, static method C calls.

    If you get a class field is constant with ConstantValue property, does not trigger initialization

  • The first call java.lang.invoke.MethodHandlereturns an instance of the REF_getStatic, REF_putStatic, REF_invokeStatic, REF_newInvokeSpecialmethod handle type.

  • Reflection calls, such as Class java.lang.reflect` classes in the package

  • If C is a class that subclasses <clinit>before the method call, the first call C <clinit>method

  • If C is an interface, and a non-defined abstract, non- staticmethod, the implementation of which (directly or indirectly) performs initialization method <clinit>will first initialized C.

  • C as the main class (containing the main method)

It can be seen perform some time-consuming operation will cause blocking even class initialization failure in the static {}

Before class initialization, the operation will first link

To speed up the initialization efficiency, jvm is multi-threaded execution initialization operation, there may be more than one thread at a time tries to initialize the class may also be a class initialization process has triggered a recursive initialize the class, so jvm need to ensure that only one thread to initialize action, jvm by maintaining a state and a mutex is already proven class to ensure the initialization process is thread-safe.

Class virtual machine state:

  • Class has been verified and ready, but not initialized
  • Class is initialized by a thread
  • Class initialization has been completed, you can use
  • Class initialization failure

In fact, the state of the virtual machine class definition may be more than four kinds of the above, such as Hotspot, see the foregoing

In addition to the state, before initializing a class, must first obtain a lock associated with this class of objects (monitor), referred to as LC.

C class or interface initialization process is as follows (jvm1.8 Specification):

  1. LC acquisition of waiting for the lock C.

  2. If C is initialized by another thread, the release of LC, C and blocks the current thread until the initialization is complete.

    Thread interrupts no influence on the initialization process

  3. If the current thread is being initialized C, C is certainly trigger initialization in the initialization time and recursion. LC release and return to normal.

  4. If the state C is initialized, the release of LC and return to normal.

  5. If the state failed to initialize for C, releasing LC and throw a NoClassDefFoundErrorexception.

  6. Otherwise, record the current Class C status is initialized and set the current thread to initialize the thread, and then release the LC.

    Then, in order to initialize the bytecode file C each with an ConstantValueattribute final staticfield.

    ** Note: ** jvm specification defined constants of the assignment in the initialization phase, <clinit>before the implementation of the specific implementation may not be strictly adhered to. The hotspot created virtual machine bytecode parsing process _java_mirroris assigned for each constant time image field type.

  7. Next, if C is a class and its parent class has not been initiated, SC referred to as its parent class, SI1, ..., SInreferred to as implemented in C comprising at least one non-abstract, non-static method of the interface (directly or indirectly). Initialize SC, all orders parent interface in the order recursive instead determine the order of the inheritance hierarchy for the interface I a directly implemented in C (according to the list of interfaces C of interfacesthe order), before I initialization, the first loop through the initialization I the parent interface (I interface according to the list interfacesorder).

  8. Next, view the custom class loader is turned assertion (for debugging).

    // ClassLoader
    
    // 查询类是否开启了断言
    // 通过#setClassAssertionStatus(String, boolean)/#setPackageAssertionStatus(String, boolean)/#setDefaultAssertionStatus(boolean)设置断言
    boolean desiredAssertionStatus(String className);
    复制代码
  9. Next, perform the initialization method C <clinit>.

  10. If the initialization C is normally completed, access to LC and C status is marked as initialized, wakes up all waiting threads, release the lock LC, the initialization process is complete.

  11. Otherwise, the initialization method must throw an exception E. If E is not Erroror subclass, create an ExceptionInInitializerErrorinstance (in E as a parameter), the next step in this example to replace E, if not created because the memory overflow ExceptionInInitializerErrorexample, with a OutOfMemoryErrorreplacement E.

  12. Get LClabeled C initialization state is an error, notify all waiting threads, release LC, and abnormal returns by E or other alternative (see previous step).

Virtual machine could optimize this process, it can determine when the initialization has been completed, canceled in step 1 to obtain a lock (at 4/5 and release the lock), provided that, according to the java memory model, all happens-before relationship when locking and optimized lock are present.

Next, look at an example:

interface IA {
	Object o = new Object();
}

abstract class Base {

	static {
		System.out.println("Base <clinit> invoked");
	}
	
	public Base() {
		System.out.println("Base <init> invoked");
	}

	{
		System.out.println("Base normal block invoked");
	}
}

class Sub extends Base implements IA {
	static {
		System.out.println("Sub <clinit> invoked");
	}

	{
		System.out.println("Sub normal block invoked");
	}

	public Sub() {
		System.out.println("Sub <init> invoked");
	}
}

public class TestInitialization {

	public static void main(String[] args) {
		new Sub();
	}
}

复制代码

Virtual machine running on a hotspot:

javac TestInitialization.java && java TestInitialization
复制代码

It can be seen initialization sequence is: 父类静态构造器 -> 子类静态构造块 -> 父类普通构造块 -> 父类构造器 -> 子类普通构造快 -> 子类构造器and the general configuration fast prior instance constructor call, regardless of the order.

Since the interface can not add on static {}, it can be generated by a look decompile the <clinit>method:

If no instance constructor class definition, the compiler will generate a default constructor with no arguments, calling the default constructor inside parent class

If the class is not static or static variables in the assignment statement code block, it is not necessary to generate<clinit>

Finally, the face several related questions:

  1. Here's what the code output?

    public class InitializationQuestion1 {
    
        private static InitializationQuestion1 q = new InitializationQuestion1();
        private static int a;
        private static int b = 0;
    
        public InitializationQuestion1() {
            a++;
            b++;
        }
    
        public static void main(String[] args) {
            System.out.println(InitializationQuestion1.a);
            System.out.println(InitializationQuestion1.b);
        }
    }
    复制代码

    The statement into the back of b q it? What is the output?

  2. Here's what the code output?

    abstract class Parent {
        static int a = 10;
    
        static {
            System.out.println("Parent init");
        }
    }
    
    class Child extends Parent {
        static {
            System.out.println("Child init");
        }
    }
    
    public class InitializationQuestion2 {
        public static void main(String[] args) {
            System.out.println(Child.a);
        }
    }
    复制代码

    Try the following change:

    abstract class Parent {
        static final int a = 10;
    
        static {
            System.out.println("Parent init");
        }
    }
    复制代码

    Try this again changed to the following:

    abstract class Parent {
        static final int a = value();
    
        static {
            System.out.println("Parent init");
        }
    
        static int value(){
            return 10;
        }
    }
    复制代码

Guess you like

Origin juejin.im/post/5dc0c650f265da4d310743b5