JVM类加载过程

JVM类加载过程教程

这个加载和我们标题里面的类加载,不是一个概念,这边的加载只是类加载的一个阶段。在加载阶段,虚拟机规范要完成以下三件事情:

  1. 通过一个 的全限定名来获取定义此类的二进制字节流。

    • 从 ZIP 包获取

    • 从网络中获取

    • 运行时计算生成,比如代理

    • 由其它文件生成,比如 JSP 文件。

  2. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。

  3. 在内存中生产一个代表这个类的 java.lang,Class 对象,作为方法区这个类的各种数据的访问入口。

验证

验证的主要目的是为了确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害当前的虚拟机的自身安全。验证包含 4 个阶段:

文件格式验证

当前的验证主要是保证输入的字节流能正确地解析并存储于方法区之内,格式上符合描述一个 Java 类型信息的要求。这边会将二进制流文件存入到方法区中,后面的三个方法验证都是基于方法区进行的。

元数据验证

对类的元数据信息进行语义校验,确保不存在不符合 Java 语言规范的元数据信息,比如:类中的子类覆盖了父类中 final 类型的字段或者方法。

字节码验证

这个过程主要的是校验类中的方法体,保证方法体在运行的时候不会做出危害虚拟机的安全的事件。比如:跳转指令跳转到方法体以外的字节码指令上,这个是肯定不可以的。

符合引用验证

这个过程主要是将符号引用转化为直接引用的时候,这个转换将在下面的解析过程中发生。它会验证:

  • 符号引用中的通过字符串描述的全限定名是否能找到对应的类

  • 在指定类中是否存在符合方法的字段描述符以及简单名称所描述的 方法 和字段

  • 符号引用中的类、字段、方法的访问权限是否可以被当前的类访问。

准备

准备阶段是正式为类变量分配内存并且设置类变量(被 static 修饰的变量)初始值(数据类型的 0 值)的阶段,这些变量所使用的内存都将在方法区里面进行分配。基本数据类型对应的 0 值如下:

数据类型 零值 数据类型 零值
int 0 boolean false
long 0L float 0.0f
short (short)0 double 0.0d
char ‘\u0000’ reference null
byte (byte)0

解析

把类中的符号引用转化为直接引用(比如说方法的符号引用,是有方法名和相关描述符组成,在解析阶段,JVM 把符号引用替换成一个指针,这个指针就是直接引用,它指向该类的该方法在方法区中的内存位置)。

初始化

初始化阶段是类加载过程的最后一步,到了初始化阶段,才是真正的执行类中定义的 Java 程序代码。

在准备阶段中,变量已经赋过一次系统要求的初始值,在初始化阶段,根据程序员的指定数值来初始化类的变量和其他资源。

  • 类初始化主要是对类的变量和静态语句块中的语句进行操作,编译器收集的顺序是由语句在源文件中出现的顺序决定的,静态语句块只能够访问到定义在静态语句块之前的变量,定义在它之后的变量,在前面的静态语句块中可以赋值,但是不能够访问。
  • 在执行子类的初始化时,父类已经执行完毕了。所以父类中的定义的静态语句块要优先于子类的变量赋值操作。
  • 接口与类是不同的,接口不需要先执行父类。
  • 虚拟机会保证一个类的方法在多线程的环境中能够被正确的加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程初始化这个类,其他的线程会阻塞等待,直到当前的线程执行完毕。