我们都知道类最初对应的class字节码文件,那么字节码文件到JVM使用共经历那几个过程呢?
总览图
类加载过程总览图如下图:
类加载主要分为5个过程:加载、验证、准备、解析、初始化,其中验证、准备、解析统称为连接过程。
上图中也简单描述了每个过程做的主要任务,接下来详细解释各个过程的任务。
加载阶段
加载分为3个阶段:
1、通过类的全限定名或者类的二进制字节流,JVM并没有规定字节流一定要用某种方式,可以通过压缩包(jar、war包等)、从网络上获取、动态代理生成、其他文件(JSP)、数据库、加密文件(防反编译)等。
2、将字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3、在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
总结这个过程就是加载二进制然后转换成JVM需要的结构,最后生成对应Class对象。
验证阶段
验证阶段分成4个验证过程:
1、文件格式验证:验证字节流是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理。在之前的文章中详细的分析class文件结构,这一步就是验证字节流的文件结构是否正确。
2、元数据验证:结构正确了,这一步就是验证内容是否正确,比如里面定义的数据类型是不是都属于JVM所规定。
3、字节码验证:验证字节码文件方法中的Code结构,就是验证所编写的方法是否正确合理。是整个验证过程中最复杂的一个阶段,主要目的是通过数据流分析和控制流分析,确定程序语义是合法的、符合逻辑的。。
4、符号引用验证:校验行为发生在虚拟机将符号引用转化为直接引用的时候,这个转化动作将在解析阶段中发生,验证是否有访问某些外部类、方法、字段的权限。
整个过程可以理解为:整体结构、结构类型、结构内容、外部引用4个步骤。
剩余3个阶段
准备阶段:为类的静态变量分配内存并设置初始值。我们都知道类中的静态变量是与类一起的,并不需要初始化类的对象进行访问,所以在这个阶段把这些变量所使用的内存在方法区中进行分配,方法区是一个逻辑上的区域,在JDK 7及之前,HotSpot使用永久代来实现方法区时,实现是完全符合这种逻辑概念的;而在JDK 8及之后,类变量则会随着Class对象一起存放在Java堆中。
解析阶段:Java虚拟机将常量池内的符号引用替换为直接引用的过程。
符号引用:class文件中常量池的CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info这几个结构所存储的字符串常量。
直接引用:能定位到所引用的真正内容。
以上概念只是简单理解,详细理解网上较多,可以自行查看。
初始化阶段:就是执行类构造器()方法的过程。所谓()方法的过程就是用户在类中定义的常量值赋值、静态代码块执行过程。在准备阶段已经对常量值设置初始值,在这里就是对常量设置用户定义的值,比如在类中存在如下一行代码:
public static final int i = 5;
在准备阶段是令i=0,而在初始化阶段则是令i=5的过程。这个过程也是静态代码块的执行过程。
总结
注意这几个过程不全是按照以上固定的流程进行的,比如验证阶段的符号引用验证则是在解析阶段中发生。
通过这5个过程,类被使用的准备工作就完成了,接下来才可以直接对类进行使用,比如使用类的静态常量、new一个类的实例等操作。
Java程序员日常学习笔记,如理解有误欢迎各位交流讨论!
领取专属 10元无门槛券
私享最新 技术干货