0. 加载

加载阶段的官方描述:

加载阶段是类加载过程的第一个阶段。在这个阶段,JVM 的主要目的是将字节码从各个位置 (网络、磁盘等)转化为二进制字节流加载到内存中,接着会为这个类在 JVM 的方法区创 建一个对应的 Class 对象,这个 Class 对象就是这个类各种数据的访问入口

1. 验证

JVM 加载完字节码并创建对应的 Class 对象之后,便会对该字节流进行校验,只有符合 JVM 字节码规范的文件才会被正确执行。大概分为以下部分:

  • JVM 规范校验:检查字节流的格式是否符合规范,比如魔数是否为 cafe babe, 版本是 否能被 JVM 处理等等
  • 代码逻辑校验:对代码组成的数据流和控制流进行校验,确保 JVM 运行该字节码文件后 不会出现致命错误,比如参数和返回值的类型不正确等

2. 准备

JVM 在准备阶段为类变量分配内存以及初始化,我们需要关注的是内存分配的对象和初始化 的类型

  • 内存分配的对象:Java 中的变量分为类变量和类成员变量,用 static 关键字修饰的为 类变量,其他都是类成员变量。在准备阶段 JVM 只会为类变量分配内存,而类成员变量 的内存分配会在初始化阶段进行
  • 初始化的类型:在准备阶段的初始化指的是为变量赋予 Java 语言中 该数据类型的零值, 除非该变量用 final 修饰,意味着变量不会再做修改,那么才会用用户赋予的值进行初 始化

3. 解析

当通过准备阶段之后,JVM 针对类或接口、字段、类方法、接口方法、方法类型、方法句柄 和调用点限定符 7 类引用进行解析,这个阶段的主要任务是将其在常量池中的符号引用替 换成其在内存中的直接引用

4. 初始化

到初始化阶段,用户的 Java 代码才真正开始执行,JVM 会根据语句执行顺序对类对象进行 初始化,一般来说当 JVM 遇到下面 5 种情况的时候会触发初始化:

  1. 遇到 new、getstatic、putstatic、invokestatic 这四个字节码指令时,如果类没有进 行过初始化,则需要先触发其初始化
  2. 使用 java.lang.reflect 包的方法对类进行反射调用的时候,如果类没有进行过初始化, 则需要先触发其初始化
  3. 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的 初始化
  4. 当虚拟机启动时,用户需要指定一个要执行的主类(包含 main 方法的类),虚拟机会先 初始化这个主类
  5. 当使用 JDK1.7 动态语言支持时,如果一个 java.lang.invoke.MethodHandle 实例最后 的解析结果为 REF_getstatic, REF_putstatic, REF_invokeStatic 的方法句柄,并且 这个方法句柄所对应的类没有进行初始化,则需要先出触发其初始化

5. 使用

JVM 从入口方法(main)开始执行用户的程序代码

6. 卸载

当用户程序代码执行完毕后,JVM 便开始销毁创建的 Class 对象,最后负责运行的 JVM 也 退出内存