《深入理解java虚拟机-JVM高级特性与最佳实践》读书笔记 虚拟机类加载机制
1. 概述
虚拟机的类加载机制:虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和出初始化,最终形成可以被虚拟机直接使用的java类型。
类型的加载、连接与初始化过程都是在程序运行期完成的。虽然令类加载时稍微增加一些性能开销,但是为java应用程序提供高度的灵活性。 java里天生可以动态扩展的语言特性就是依赖运行期动态加载和动态连接这个特殊实现的。
2.类加载的时机
类的生命周期
图中,加载、验证、准备、初始化和卸载这5个阶段的顺序是确定的,类的加载过程必须按照这种顺序按部就班的开始。
而解析阶段则不一定,,他在某些情况下可以在初始化阶段之后再开始。,这事为了支持java语言的运行时绑定(也称为动态绑定或晚期绑定)。
虚拟机规范严格规定了有且只有5种情况必须立即对类进行“初始化”,:
1.使用new关键字实例化对象时、读取或设置一个类的静态字段(被final修饰、已在编译器把结果放入常量池的静态字段除外)时, 以及调用一个类的静态方法时
2.使用Java.lang.reflect包的方法对类继续宁反射调用时
3.初始化一个类时,发现其父类还没有继续宁过初始化,则需要先出发期父类的初始化
4.当虚拟机启动时,用户需要制定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类
5.当使用jdk1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且 这个方法句柄对应的类没有进行过初始化,则需要先出发其初始化。
这5个场景的行为称为对一个类进行主动引用。
被动引用的方式不会触发初始化:
1.通过子类引用父类的静态字段,不会导致子类初始化
package ClassLoading;
/**
* 被动使用类字段演示一
* 通过子类引用父类的静态字段,不会导致子类初始化
* @author lxp
* @version 1.0
* @date 2020/2/2 18:57
*/
public class SuperClass {
static {
System.out.println("SuperClass init");
}
public static int value = 123;
}
package ClassLoading;
/**
* @author lxp
* @version 1.0
* @date 2020/2/2 18:59
*/
public class SubClass extends SuperClass {
static {
System.out.println("SubClass init");
}
}
package ClassLoading;
/**
* 非主动使用类字段演示
* @author lxp
* @version 1.0
* @date 2020/2/2 19:00
*/
public class NotInitialization {
public static void main(String[] args) {
System.out.println(SubClass.value);
}
}
演示结果:
"D:\Program Files\java\jdk1.8\bin\java.exe" "-javaagent:D:\codeSoftWare\idea\IntelliJ IDEA 2019.2.1\lib\idea_rt.jar=50173:D:\codeSoftWare\idea\IntelliJ IDEA 2019.2.1\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\java\jdk1.8\jre\lib\charsets.jar;D:\Program Files\java\jdk1.8\jre\lib\deploy.jar;D:\Program Files\java\jdk1.8\jre\lib\ext\access-bridge-64.jar;D:\Program Files\java\jdk1.8\jre\lib\ext\cldrdata.jar;D:\Program Files\java\jdk1.8\jre\lib\ext\dnsns.jar;D:\Program Files\java\jdk1.8\jre\lib\ext\jaccess.jar;D:\Program Files\java\jdk1.8\jre\lib\ext\jfxrt.jar;D:\Program Files\java\jdk1.8\jre\lib\ext\localedata.jar;D:\Program Files\java\jdk1.8\jre\lib\ext\nashorn.jar;D:\Program Files\java\jdk1.8\jre\lib\ext\sunec.jar;D:\Program Files\java\jdk1.8\jre\lib\ext\sunjce_provider.jar;D:\Program Files\java\jdk1.8\jre\lib\ext\sunmscapi.jar;D:\Program Files\java\jdk1.8\jre\lib\ext\sunpkcs11.jar;D:\Program Files\java\jdk1.8\jre\lib\ext\zipfs.jar;D:\Program Files\java\jdk1.8\jre\lib\javaws.jar;D:\Program Files\java\jdk1.8\jre\lib\jce.jar;D:\Program Files\java\jdk1.8\jre\lib\jfr.jar;D:\Program Files\java\jdk1.8\jre\lib\jfxswt.jar;D:\Program Files\java\jdk1.8\jre\lib\jsse.jar;D:\Program Files\java\jdk1.8\jre\lib\management-agent.jar;D:\Program Files\java\jdk1.8\jre\lib\plugin.jar;D:\Program Files\java\jdk1.8\jre\lib\resources.jar;D:\Program Files\java\jdk1.8\jre\lib\rt.jar;E:\coding\Practice\out\production\Practice;E:\coding\Practice\lib\asm-5.0.3.jar;E:\coding\Practice\lib\cglib-2.2.jar;E:\coding\Practice\lib\asm-all-7.0.1.jar;E:\coding\Practice\lib\asm-tree-5.0.3.jar;E:\coding\Practice\lib\cglib-nodep-3.2.4.jar;E:\coding\Practice\lib\asm-analysis-5.0.3.jar" ClassLoading.NotInitialization
SuperClass init
123
Process finished with exit code 0
对于静态字段,只有直接定义这个字段的类才会被初始化,因此通过其子类来引用父类中定义的静态字段,指挥触发弗雷德初始化而不会触发子类的初始化。
通过-XX:+TraceClassLoading参数来观察加载顺序。
演示二: