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 运行时的核心之一,理解它有助于优化内存管理和类加载行为。

相关推荐
奋进的小暄18 分钟前
数据结构(java)栈与队列
java·开发语言·数据结构
SnXJi_19 分钟前
纷析云开源财务软件:企业财务数字化转型的灵活解决方案
java·gitee·开源·开源软件
pingzhuyan44 分钟前
03(总)-docker篇 Dockerfile镜像制作(jdk,jar)与jar包制作成docker容器方式
java·docker·jar
笺上山河梦1 小时前
文件操作(二进制文件)
开发语言·c++·学习·算法
大慕慕好懒2 小时前
PHP弱类型hash比较缺陷
算法·哈希算法
caihuayuan42 小时前
Redis奇幻之旅(三)1.redis客户端与服务端
java·大数据·sql·spring·课程设计
snowfoootball2 小时前
最短路问题
数据结构·算法
_一条咸鱼_3 小时前
Vue 配置模块深度剖析(十一)
前端·javascript·面试
匆匆整棹还3 小时前
关于tomcat乱码和idea中控制台乱码的问题
java·tomcat·intellij-idea
怀旧,3 小时前
【数据结构】4.单链表实现通讯录
android·服务器·数据结构