JVM 为什么需要类加载机制?深入浅出 JVM 类加载原理

开发 前端
在 Java 中,类加载机制是 Java 虚拟机(JVM)将 .class 文件加载到内存并转化为可以运行的 Class 对象的过程。简单来说,类加载机制是让“代码变为现实”的第一步!

类加载机制是什么?

在 Java 中,类加载机制是 Java 虚拟机(JVM)将 .class 文件加载到内存并转化为可以运行的 Class 对象的过程。简单来说,类加载机制是让“代码变为现实”的第一步!

你可能会问,为什么需要类加载机制? 因为 Java 是一门 动态语言,类可以在运行时加载、链接和初始化,这种灵活性让 Java 能够实现跨平台运行、高效的内存管理和模块化架构。

类加载的三个阶段

根据《Java 虚拟机规范》,类的生命周期包括以下三个主要阶段:加载链接初始化

而其中链接又分为三个子阶段:验证(Verification)、准备(Preparation)、解析(Resolution)。

图片图片

我们逐一拆解这些阶段的工作原理和流程。

加载(Loading)


Chaya:类加载阶段作用是什么?非要加载吗?

主要是使用 "类加载器" 将本地或者远程网络中的字节码文件,通过读字节流的方式加载到 Java 虚拟机内存中。在加载阶段中 Java 虚拟机主要完成以下三件事情:

  • 通过一个类的全限定名称来获取定义此类的二进制字节流。
  • 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
  • 在内存中生成一个代表这个类的 java.lang.Class 对象,作为方法区中这个类的各种数据的访问入口。

加载是类加载的第一步,JVM 需要完成以下任务:

图片图片

  1. 读取 Class 文件:通过类的全限定名找到对应的 .class 文件。
  2. 转换为 JVM 可识别的结构:将 Class 文件的二进制数据转换为 JVM 的运行时数据结构。
  3. 创建 Class 对象:在内存中创建 java.lang.Class 对象,作为该类的入口。

示例。

Class<?> clazz = Class.forName("com.example.MyClass");

这段代码会触发 MyClass 的加载,将其 .class 文件读取到内存中,并生成 Class 对象。

链接(Linking)

链接 是将 Class 文件中的符号引用解析为直接引用的过程,分为以下三个子阶段:

  1. 验证(Verification)确保 Class 文件的字节码格式和内容符合 JVM 的规范。

验证文件格式:Class 文件是否以 0xCAFEBABE 开头。

验证字节码:指令是否符合 JVM 规范,数据类型是否匹配。

  1. 准备(Preparation)为类的静态变量分配内存,并设置默认值。

例如:static int a = 10; 在准备阶段,a 的初始值是 0

  1. 解析(Resolution)将符号引用替换为内存地址的直接引用。

符号引用java.lang.String

直接引用:指向 String 类在内存中的地址。

验证阶段 (Verification)

验证阶段的主要目的是对字节码字节流进行校验,判断其内容是否符合当前虚拟机的规范,以确保被加载的代码运行后不会对虚拟机造成损害。

大多数虚拟机大致都会对 文件格式元数据字节码符号引用 几项内容进行校验。

文件格式验证

文件格式验证主要是对 字节流格式 进行校验,判断其是否符合字节码文件格式规范,并且还要判断其是否可以运行在当前版本的虚拟机中。比如:

序号

描述

1

验证是否以 0XCAFEBABE 开头

2

验证主、次版本号,是否包含在当前虚拟机支持的版本范围内

3

验证字节码常量池中的常量类型,是否都被虚拟机所支持

4

验证指向常量的各种索引值,是否有指向不存在的常量或不符合类型的常量

5

验证 CONSTANT_Utf8_info 类型常量中,是否有不符合 UTF-8 编码的数据

6

验证字节码文件中各个部分及文件本身,是否有被删除或附加的其他信息

文件格式验证的主要目的其实就是为了保证加载的字节码可以被正确地解析并存储在方法区内。

元数据验证

元数据验证主要是对 字节码 中的 元数据信息 进行语法校验,避免存在不符合 Java 语法规范的元数据信息。比如:

序号

描述

1

验证当前类的父类是否继承了不允许被继承的类,比如被 final 修饰的类

2

验证当前类是否有父类,一般情况下除了 java.lang.Object 外,所有的类都应当有父类

3

验证如果当前类不是抽象类,则当前类是否实现了其父类或接口之中要求实现的所有方法

4

验证当前类中的字段或方法是否与父类有冲突,比如当前类覆盖了父类的 final 字段,或者当前类实现的方法参数都一致,但返回值的类型却不同,导致不符合方法重载规则等情况

字节码验证

字节码验证主要是对 数据流控制流 进行分析,以确保其语法合规且符合逻辑。

符号引用验证

符号引用验证主要对 字节码常量池常量 的各种 符号引用 进行校验,确保当前类引用到的其它类或者方法是真实存在且有权限访问的。如果符号引用中关联的类无法在系统中查找到,就会抛出 NoClassDefFoundError 错误,如果符号引用中关联的方法无法找到,则会抛出 NoSuchMethodError 错误。

准备阶段 (Preparation)

准备阶段主要是用于对类或接口中的 "静态变量" 分配内存空间,以及对变量设置默认的初始值。

准备阶段和初始化阶段,这两个阶段都是用于对静态变量设置值,概念上容易混淆,所以这里需要特别说明一下,准备阶段只是对静态变量设置初始默认值,而真正赋值操作是在初始化阶段完成的。

例如,下面示例代码在执行时:

public class A {
    static int test = 999;
}
  • 准备阶段会对变量 test 设置默认值 0
  • 初始化阶段会对变量 test 赋予初始值 999

解析阶段 (Resolution)

解析阶段主要是用于将 字节码常量池 中的 符号引用 替换为 直接引用 的过程。

  • 符号引用 (Symbolic References): 符号引用就是用于描述引用目标的一组符号,它可以是任何形式的字面量 (只要符合 Java 虚拟机规范)。
  • 直接引用 (Direct References): 直接引用可以是直接指向目标的指针、相对偏移量,或者是一个能间接定位到目标的句柄。

初始化(Initialization)

  1. 初始化阶段是类加载的最后一步,也是最重要的阶段。此阶段会执行静态变量的赋值操作和静态代码块。
    初始化的触发条件
    类的初始化顺序

先初始化父类

再初始化当前类的静态变量和静态代码块

使用 new 关键字实例化对象时

访问类的静态字段或静态方法时

使用反射调用类时


唐二婷:初始化阶段有啥用?可以谈恋爱吗?

初始化阶段主要是执行 类构造器 方法 <clinit>(),该方法不需要定义,代码在经过 Javac 编译器编译时,会自动收集类中的所有 类变量 的赋值动作和 静态代码块 中的语句,对这些代码进行合并,形成类构造器 <clinit>()

在执行类构造器 <clinit>() 时,会对类中的 类变量静态代码块 进行初始化赋值操作,如果该类存在父类,则会先执行父类中的类构造器 <clinit>(),对父类中的 类变量静态代码块 进行初始化。

示例如下。

public class FatherCLass {

    public static int number;

    static {
        System.out.println(number);
        System.out.println("父类 static{} 初始化");
    }

}

子类:

public class SubInitialization extends FatherCLass {

    static{
        // number 属于父类的属性,这里要能执行成功,说明父类已经加载
        number = 100;
        System.out.println("子类 static{} 初始化");
    }

    public static void main(String[] args) {
        System.out.println(number);
    }

}

执行时输出如下:

0
父类 static{} 初始化
子类 static{} 初始化
100


责任编辑:武晓燕 来源: 码哥跳动
相关推荐

2023-10-31 16:00:51

类加载机制Java

2019-10-10 16:25:02

JVM数据多线程

2021-09-24 08:10:40

Java 语言 Java 基础

2021-10-05 20:29:55

JVM垃圾回收器

2023-08-02 08:38:27

JVM加载机制

2017-09-20 08:07:32

java加载机制

2023-05-05 18:33:15

2024-03-12 07:44:53

JVM双亲委托机制类加载器

2017-03-08 10:30:43

JVMJava加载机制

2021-04-29 11:18:14

JVM加载机制

2012-05-21 09:51:25

对象Cocoa

2012-05-21 09:58:30

动态创建类Cocoa

2022-10-08 08:34:34

JVM加载机制代码

2021-07-20 15:20:02

FlatBuffers阿里云Java

2020-10-26 11:20:04

jvm类加载Java

2024-03-08 08:26:25

类的加载Class文件Java

2024-12-04 09:01:55

引导类加载器C++

2020-05-20 22:13:26

JVM加载机制虚拟机

2019-12-30 11:25:06

Jvm运行java

2024-04-09 08:41:41

JVM类加载Java
点赞
收藏

51CTO技术栈公众号