把描述类的数据从Class文件加载到内存,并且对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型。

类加载生命周期

  • loading, (加载), bring binary form of a type into JVM
  • linking, (连接), incorporating the binary type data into the JVM runtime state
    • Verification, 验证, ensure type is properly formed and fit
    • Preparation, 准备, allocating the memory needed by the type
    • Resolution, 解析, transforming symbolic reference in constant pool into direct reference
  • Initialization, 初始化, the class variabes are given their proper initial value

  • Using, 使用

  • Unloading, 卸载

lifecycle = loading , linking, initialize

linking(连接) = 验证 + 准备 + 解析

image.png

加载

类的加载时机(虚拟机没有进行规定,由各虚拟机自己把握)

  • 找数据: 通过一个类的全限定名来获取该类的二进制流
  • 转换:二进制流的静态存储结构 ——> 运行时的数据结构(在方法区中,由虚拟机自己实现)
  • 内存java.lang.class对象,作为入口

方法区

  • 存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
  • Non-Heap
  • 永久代Permanent Generation

连接

连接 = 验证 + 准备 + 解析

验证

为了确保class文件的字节流包含的信息符合当前虚拟机要求,不会危害虚拟机自身安全

纯粹的java代码无法做到

  • 文件格式验证
    • 是否以魔数开头
    • 版本号
  • 元数据验证
  • 字节码验证
  • 符号引用验证
    • make sure the reference exist and have access permission

准备

no java code execute

为类变量分配内存并且设置初始值, 仅仅是static变量

  • set to default 0
  • allocate the memory for improve the performance(扩充方法区)

解析

将常量池的符号引用替换为直接引用

locating classes, interfaces, fields, methods from constant pool, and replace to direct reference.

  • 通过全限定名是否能找到对应的类
  • 是否存在符合方法的字段描述符以及简单名称所描述的方法和字段
  • 类、字段、方法访问性等等

初始化

setting the class variables to the proper initial value

  • via a class variable initializer
  1. static int size = 3 * (int) (Math.random() * 5.0);
  • via static initializer
  1. static int size;
  2. // This is the static initializer
  3. static {
  4. size = 3 * (int) (Math.random() * 5.0);
  5. }

类的初始化时机(虚拟机规范有严格的规定),initialize on first active used

  • new, getstatic, putstatic, invokestatic这四条字节码指令(new对象, 读取设置类的静态字段,调用类的静态方法)
  • 对类进行反射调用的时候
  • 初始化子类,要初始化父类
  • 虚拟机启动时,需要初始化主类(Main方法)
  • 使用动态语言支持时,java.lang.invoke.MethodHandler实例最后解析结果的方法句柄所对应的类没有进初始化
    • REF_getStatic
    • REF_putStatic
    • REF_invokeStatic

注意,初始化并不代表着调用构造函数

class initialize method

all class variable initializer and static initializer woudl be collected by compiler and place into a speicial method call class intialization method “<clinit”

two steps

  • 初始化父类(如果有父类)
  • 执行”<clinit”(如果有)
    • 没有类变量则不会有”<clinit”函数
    • 有类变量但是没有initializer,则不会有”<clinit”函数
    • 有类变量有initializer,但是使用compile-time constant expression初始化,也不会有”<clinit”函数