此处我选取的OpenJDK12的版本,下载地址:hg.openjdk.java.net/jdk/jdk12/f… 点击zip即可下载。
所谓的Java虚拟机的类加载机制,就是Java虚拟机从Class文件中取得类的数据并加载到内存中进行一系列的操作后,可以直接让虚拟机使用的Java类型。
何时进行类加载
对于一个类而言(当然,一个接口也是如此。),它的整个生命周期可以分为加载、验证、准备、解析、初始化、使用和卸载七个阶段。
在《Java虚拟机规范》(见文末)里,规定了必须立即对六种情况进行“初始化操作”。我们也把这六种情况称为“主动引用”。
- 当遇到new、getstatic、putstatic、invokestatic这四条字节码指令时,应当进入初始化阶段。
- 使用java.lang.reflect时进行反射类
- 对一个类进行初始化时,若发现其父类仍未初始化,则先对其父类进行初始化。
- 优先初始化包含main方法的类
- 使用动态语言支持时,如果java.lang.invoke.MethodHandle实例最后的解析结果为REF_getStatic、REF_putStatic、REF_invokeStatic、REF_newInvokeSpecial四种类型的方法句柄时,且方法句柄对应的类没能进行初始化,则先使该类进行初始化
- 如果一个被default修饰的接口的实现类要被初始化,那么该接口会在其之前先进行初始化。
相对应的,除此之外的所有引用类型就被称为“被动引用”。
类加载的过程
我们先从“加载”说起。
一. 加载
加载是整个“类加载”里面的第一个阶段。在这个阶段,Java虚拟机完成了三件事情: ① 通过类的全限定名来获取其二进制字节流 ②将二进制字节流的静态存储结构转化为方法区的运行时数据结构 ③内存中生成一个代表类的java.lang.Class对象,作为这个类的各种数据的访问入口
验证、准备和解析三个阶段,又被统称为连接。我们谈论连接的第一步:验证。
二. 验证
验证是连接的第一步,其目的是为了确保Class文件中的字节流合乎虚拟机规范的约束要求,以防止这些代码在运行之后会对虚拟机造成危害。验证阶段是十分重要的。而验证阶段大概会完成以下四个阶段的检验动作:文件格式验证、元数据验证、字节码验证和符号引用验证。
1. 文件格式验证
主要验证字节流是否符合Class文件格式的规范,并且要能够符合当前版本的虚拟机。 这个验证点包括但不限于:验证魔数是否以咖啡宝贝开头,常量池中的常量是否存在不支持的常量类型等。 详情可见: gitee.com/sitr/openJD…
2. 元数据验证
完成了第一步文件格式验证后,便意味着这段字节流已经进入了Java虚拟机的方法区中了,所以后面的三个验证阶段都是基于方法区的存储结构上进行的。 元数据验证是对字节码描述的信息进行语义分析,以保证符合虚拟机规范的要求。该阶段包含的验证点包括但不限于:该类是否有父类、该类如果不是抽象类则是否实现了父类中的所有方法等。
3. 字节码验证
第三阶段是整个验证过程最复杂的一环了。主要目的是通过数据流分析和控制流分析,来确定语义的合法。第二阶段是对元数据信息中的数据类型进行校验,而这阶段则是开始对类的方法体进行校验,以保证虚拟机的安全。 但是即使是景观字节码验证,也仍然不能确保安全的。
在Javac编译器里面,也有多项校验辅助措施。其结果是在方法体的Code属性(不晓得啥是Code方法体?可参见类文件结构)的属性表中新增了一项“StackMapTable”的新属性。
4. 符号引用验证
《Java虚拟机规范》中文版pdf:链接: pan.baidu.com/s/1RVLCLYI-… 提取码: 7usa