JVM当中的类加载

类加载机制

不同于其他Java基础知识,类加载机制我们可能接触得比较少,之前我们了解到Java源代码先是被编译成class字节码,最终需要加载到虚拟机中才能运行。

整个生命周期包括:加载、验证、准备、解析、初始化5个阶段。

其中加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。

加载:查找并加载类的二进制数据

1、通过一个类的全限定名获取描述此类的二进制字节流;
2、将这个字节流所代表的静态存储结构保存为方法区的运行时数据结构;
3、在java堆中生成一个代表这个类的java.lang.Class对象,作为访问方法区的入口;

验证:确保被加载的类的正确性

文件格式验证:验证字节流是否符合Class文件格式的规范;例如:是否以0xCAFEBABE开头、主次版本号是否在当前虚拟机的处理范围之内、常量池中的常量是否有不被支持的类型。

元数据验证:对字节码描述的信息进行语义分析(注意:对比javac编译阶段的语义分析),以保证其描述的信息符合Java语言规范的要求;例如:这个类是否有父类,除了java.lang.Object之外。

字节码验证:通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。

符号引用验证:确保解析动作能正确执行。

准备:为类的静态变量分配内存,并将其初始化为默认值。

假设一个类变量的定义为:public static int value = 3;

那么变量value在准备阶段过后的初始值为0,而不是3,因为这时候尚未开始执行任何Java方法,而把value赋值为3的putstatic指令是在程序编译后,存放于类构造器<clinit>()方法之中的,所以把value赋值为3的动作将在初始化阶段才会执行。

解析:把类中的符号引用转换为直接引用

1、符号引用使用一组符号来描述所引用的目标,可以是任何形式的字面常量,定义在Class文件格式中。
2、直接引用可以是直接指向目标的指针、相对偏移量或则能间接定位到目标的句柄。

初始化:为类的静态变量赋予正确的初始值

JVM中只有5种情况必须对类进行初始化

以下几种情况,不会触发类初始化

1.通过子类引用父类的静态字段,只会触发父类的初始化,而不会触发子类的初始化。

  • 执行new、getstatic、putstatic和invokestatic指令;
  • 使用reflect对类进行反射调用(如Class.forName);
  • 初始化一个类的时候,父类还没有初始化,会事先初始化父类;
  • 启动虚拟机时,需要初始化包含main方法的类;
  • 访问某个类或接口的静态变量,或者对该静态变量赋值;

2.定义对象数组,不会触发该类的初始化。

猜你喜欢

转载自blog.csdn.net/weixin_41328649/article/details/85920353