JVM 类加载机制

JVM 类加载机制详解

JVM(Java Virtual Machine,Java 虚拟机)中的类加载机制 (Class Loading Mechanism)是指 JVM 在运行时动态加载 .class 文件,并将其转换为 JVM 识别的类对象 (Class Object),以便执行。Java 的类加载采用按需加载 (Lazy Loading)和双亲委派模型(Parent Delegation Model),确保类的安全性和避免重复加载。


1. 类加载的过程

Java 类的加载过程主要分为 五个阶段:

  1. 加载(Loading)
  2. 连接(Linking)
    • 验证(Verification)
    • 准备(Preparation)
    • 解析(Resolution)
  3. 初始化(Initialization)
  4. 使用(Using)
  5. 卸载(Unloading)

(1)加载(Loading)

在类加载 阶段,JVM 通过类加载器 (ClassLoader)从字节码文件 (.class)或其他来源(如网络、JAR包等)读取二进制数据,转换成方法区 (Method Area)中的类对象。

  • 加载的来源:

    • 本地 .class 文件
    • Jar 包
    • 网络(远程加载)
    • 动态代理生成的类
    • 其他自定义数据源
  • 主要任务:

    • 通过 类加载器 读取 .class 文件,生成二进制字节流。
    • 将字节流解析为 JVM 内部数据结构,存放在方法区。
    • 在 堆区 (Heap)中生成该类的 Class 对象,用于管理该类的元数据。

示例:手动触发类加载

复制代码
Class<?> clazz = Class.forName("com.example.MyClass"); // 反射触发类加载

(2)连接(Linking)

连接是把已经加载的类转换成可以运行的状态,包括三步:

  1. 验证(Verification) :确保 .class 文件格式正确,符合 JVM 规范,避免恶意字节码。
  2. 准备(Preparation) :为类变量 (static 变量)分配内存,并初始化默认值(不执行赋值操作)。
  3. 解析(Resolution) :把类中的符号引用转换为直接引用(指向方法区中具体的内存地址)。

示例:准备阶段

复制代码
public class Example {
    static int x = 10; // x 的默认值在准备阶段是 0,初始化阶段才会变为 10
}

(3)初始化(Initialization)

类初始化是执行静态代码的过程:

  • 执行 static 变量的赋值 和 静态代码块 (static {})。
  • 初始化的顺序 按类的继承关系 从父类到子类 依次进行。

示例:类初始化

复制代码
class Parent {
    static int a = 1;
    static { System.out.println("Parent 初始化"); }
}
class Child extends Parent {
    static int b = 2;
    static { System.out.println("Child 初始化"); }
}
public class Test {
    public static void main(String[] args) {
        System.out.println(Child.b);
    }
}

输出:

复制代码
Parent 初始化
Child 初始化
2

说明:

  • Parent 先初始化,因为 Child 继承自 Parent。
  • 只有 static 变量和 static 代码块才会在类初始化阶段执行。

(4)使用(Using)

类初始化完成后,就可以正常使用该类:

  • 创建对象
  • 调用静态方法
  • 访问静态变量

(5)卸载(Unloading)

类在以下情况下会被卸载:

  • 类的所有实例都被 GC
  • ClassLoader 被 GC
  • JVM 关闭

但是,JVM 不会卸载 Bootstrap ClassLoader 加载的类 (即 rt.jar 内的核心类)。


2. Java 类加载器(ClassLoader)

类加载器 负责将 .class 文件加载到 JVM。JVM 主要有三种类加载器:

类加载器 作用 负责加载的类
Bootstrap ClassLoader 启动类加载器 Java 核心类库(rt.jar)
Extension ClassLoader 扩展类加载器 ext 目录下的 JAR
Application ClassLoader 应用类加载器 classpath 下的类

示例:查看类加载器

复制代码
System.out.println(String.class.getClassLoader()); // null (Bootstrap 加载)
System.out.println(Test.class.getClassLoader());   // AppClassLoader

此外,Java 支持 自定义类加载器:

示例:自定义 ClassLoader

复制代码
class MyClassLoader extends ClassLoader {
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] bytes = loadClassData(name); // 自定义加载逻辑
        return defineClass(name, bytes, 0, bytes.length);
    }
}

3. 双亲委派机制(Parent Delegation Model)

工作原理

当一个 ClassLoader 需要加载类时,它不会直接加载,而是:

  1. 先委托给父类加载器。
  2. 父类加载失败(即找不到类)时,才会由子类加载器尝试加载。

作用

  • 避免重复加载 :防止 Java 核心类(如 java.lang.String)被自定义类覆盖。
  • 提高安全性:防止恶意代码篡改 Java 标准库。

示例:双亲委派

复制代码
public class Test {
    public static void main(String[] args) {
        System.out.println(Test.class.getClassLoader()); // AppClassLoader
        System.out.println(String.class.getClassLoader()); // null (Bootstrap)
    }
}

4. 类的主动引用 & 被动引用

(1)主动引用(会触发类加载)

以下情况会触发类加载:

  • 创建对象 (new 关键字)
  • 访问静态变量
  • 调用静态方法
  • 反射
  • 子类初始化时,会先加载父类

示例:主动引用

复制代码
class Parent {
    static { System.out.println("Parent 被加载"); }
}
public class Test {
    public static void main(String[] args) {
        Parent p = new Parent(); // 触发加载
    }
}

(2)被动引用(不会触发类加载)

  • 通过子类访问父类的静态变量
  • 访问 final 常量
  • Class.forName() 的 initialize=false 方式

示例:被动引用

复制代码
class Parent {
    static { System.out.println("Parent 被加载"); }
    static int a = 10;
}
class Child extends Parent {}
public class Test {
    public static void main(String[] args) {
        System.out.println(Child.a); // 仅加载 Parent
    }
}

总结

  1. 类加载分为:加载、连接(验证、准备、解析)、初始化、使用、卸载。
  2. JVM 采用双亲委派机制,确保安全性和避免重复加载。
  3. 主动引用会触发类加载,被动引用不会。

JVM 类加载机制是 Java 运行时的核心之一,理解它有助于优化内存管理和类加载行为。

相关推荐
Peregrine912 小时前
C++ - > 类和对象
java·c++·算法
2501_9339232513 小时前
SpringCloud组件:Eureka服务注册与发现
java·开发语言·后端·spring cloud·eureka
Wang's Blog13 小时前
Java 中间件之 RabbitMQ 快速入门: SpringAMQP 消息转换器
java·中间件·java-rabbitmq
Re_zero13 小时前
一次 SSE 断流排查:PrintWriter 吞掉了我的 IOException
java·后端
Gigavision13 小时前
DEAP数据集 2026最新Transformer算法 DEAP 数据集 下载
深度学习·算法·transformer
我是人✓13 小时前
Java核心语法:作用域、循环与大数值处理
java·开发语言·后端
夜之眷属14 小时前
PriorityQueue:for-each 遍历和 poll 取出,顺序一样吗?
java·服务器·数据结构·后端·算法
触底反弹14 小时前
🎯 LangSmith 学习与面试全攻略:从 Agent「盲盒」到全链路观测与量化评估
人工智能·面试
一tiao咸鱼14 小时前
还在手动海投?我做了个 AI 求职助手,自动筛选 BOSS 职位再精准投递
前端·后端·面试
蜗牛互联网14 小时前
Java标准HTTP调用多模态模型的票据抽取前置校验
java·人工智能·python·http