正文
Java有个很重要的特点:一次编译,到处运行(Write Once,Run Anywhere)。这是为什么了?看图1.1
类加载的基本流程
因为不同操作系统的JVM是不同,所以可以将字节码文件编译成不同系统适配的机器码执行。那么在这个过程是否还有其余步骤。肯定是有的,这个编译后的class文件是否符合规范?加载后的数据怎么提供给别的类使用?类的各个部分的初始化顺序?类的成员属性的初始值怎么设定和初始化后内存地址怎么绑定等等。
类加载流程
完整的类加载流程包括五个步骤:
类加载流程
加载:加载字节码文件,存储到方法区中,存储的数据格式完全由虚拟机实现自行定义。方法区数据生成完后,会在Java堆内存中实例化一个 java.lang.Class类的对象,这个对象将作为程序访问方法区中的类型数据的外部接口。
验证:确保Class文件的字节流信息符合规范,不会危害虚拟机自身的安全。主要分四个验证:
1、文件格式验证,该阶段主要目的是保证输入的字节流能正确的解析并存储与方法区。
2、元数据验证,该阶段主要对类的元数据信息进行语义校验;
3、字节码验证,该阶段主要是确保程序语义是合法的、符合逻辑的;
4、符号引用验证,该阶段的校验行为发生在虚拟机将符号引用转换为直接饮用的时候(发生在解析阶段);
对于验证阶段,如果你能确保全部代码都已经被反复使用或验证过,在生产环境的实施阶段就可以考虑使用 -Xverify:none 参数来关闭大部分的类验证措施。
准备:为类中定义的变量( static 修饰的变量)分配内存并设置类变量默认初始值(非程序中指定值),需要说明的是如果变量被final修饰,在准备阶段就是设置好程序中指定的值。
解析:Java虚拟机将常量池内的符号引用替换为直接引用的过程。举例说明下两种引用的差别:
public class Test {
public static void main(String[] args){
Parent parent = null; //parent 为符号引用,符合命名规范即可
Parent son = new Son(); //son 为直接引用,已经和虚拟机实现的内存布局直接相关
System.out.println(son); //com.mtt.classload.Son@119d7047
}
}
1、类和接口的解析;
2、字段解析;
3、方法解析;
4、接口方法解析。
初始化:到这个阶段,Java虚拟机才真正开始执行类中编写的Java程序代码。这个阶段会关联到初始化的顺序,可以参考https://www.jianshu.com/p/95cfe4984651。
参考文献:《深入理解Java虚拟机:JVM高级特性与最佳实践(第三版)》周志明著