当前位置: 首页 > 后端技术 > Java

一篇文章彻底搞懂JVM加载中初始化的时机

时间:2023-04-01 16:00:51 Java

JVM类加载过程JVM类加载过程分为加载、验证、准备、解析、初始化几个阶段。加载是将二进制字节码加载到内存中,验证是检查字节流中包含的信息是否符合要求,准备是为静态变量分配内存并设置静态变量的初始值,解析是替换中的符号引用常量池为直接引用,初始化是执行静态语句块中的所有静态变量赋值动作和语句。更详细的分析可以看之前的文章《JVM的类加载机制全面解析》,这里就不赘述了。类初始化的时机对于我们开发者来说,我觉得应该详细了解初始化阶段什么时候开始。JVM规范对此有严格规定,只有以下五种情况必须初始化类:当遇到new、getstatic、putstatic或invokestatic这四个字节码指令时,如果类还没有初始化,则需要初始化初始化先初始化。不懂字节码指令的同学可能会一头雾水。再说说人,就是:在使用new关键字实例化对象时,在读取和设置类的静态字段(不被final修饰)时,在调用类的静态方法时。这样比较容易理解。在使用java.lang.reflect包中的方法对类进行反射调用时,如果该类还没有初始化,则需要先进行初始化。在初始化一个类的时候,如果发现它的父类还没有初始化,那么需要先初始化它的父类。JVM首先会初始化要执行的主类,也就是包含main()方法的类。在使用JDK1.7的动态语言支持时,如果java.lang.invoke.MethodHandle实例的最终解析结果为REF_getStatic(使用MethodHandle读取类的静态字段),REF_putStatic(使用MethodHandle设置类的静态字段)theclass),REF_invokeStatic(usingWhenMethodHandlecallsthemethodhandleoftheclassofstaticmethod),如果方法句柄还没有初始化,需要先初始化。被动引用刚才提到的五种情况都会触发初始化。这些行为称为对类的主动引用。除了这些,所有引用类的方式都不会触发初始化,称为被动引用。为了更好地理解,这里有一些被动引用的例子。通过子类引用父类的静态变量publicclassSuperClass{static{System.out.println("父类正在初始化");}publicstaticStringname="万猫学社";}publicclassSubClassextendsSuperClass{static{System.out.println("子类正在初始化");}}publicclassOneMoreStudy{publicstaticvoidmain(String[]args){System.out.println(SubClass.name);}}对于静态变量,只有直接定义这个变量的类才会被初始化。通过子类引用父类中定义的静态变量,只会触发父类的初始化,不会触发子类的初始化。运行结果为:父类正在初始化万猫society结果中没有“子类正在初始化”。通过数组定义引用类publicclassOneMoreStudy{publicstaticvoidmain(String[]args){SuperClass[]arrays=newSuperClass[10];System.out.println("数组元素个数:"+arrays.length);}}此代码使用前面的SuperClass类来定义SuperClass类的一维数组。运行后结果为:数组元素个数:10结果中没有“正在初始化父类”,说明没有触发SuperClass类的Initialization。其实就是初始化了一个名为“[LSuperClass”的类,它是JVM自动生成的,直接继承自java.lang.Object,创建动作是由字节码指令newarray触发的。常量publicclassConstClass{static{System.out.println("正在初始化具有常量的类");}publicstaticfinalStringNAME="万茂学院";}publicclassOneMoreStudy{publicstaticvoidmain(String[]args){System.out.println(ConstClass.NAME);}}常量会在编译阶段存储在调用类的常量池中。本质上,不直接引用定义常量的类是不会触发定义常量的类的初始化的,所以运行的结果是:Wanmao的结果中没有出现“带有常量的类正在被初始化”学院。接口初始化的时机接口也有一个初始化过程,和类是一致的。但是,接口中不能使用“static{}”语句块,但编译器仍然会为接口生成一个“clinit()”类构造函数,用于初始化接口中定义的成员变量。接口初始化的时机与上述5种情况基本相同。唯一不同的是第三种情况:一个类在初始化的时候,它的父类也必须初始化,而一个接口在初始化的时候,它的父接口是不需要初始化的。只有在实际使用父接口时才会初始化,例如:引用父接口中定义的常量。结语这次我们主要分享类初始化时的情况,一共有5种情况。除了这5种情况的引文外,统称为被动引文,同时给出3个被动引文的例子。同时也提到了初始化接口和类的区别。