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

相关推荐
IT成长史4 分钟前
deepseek梳理java高级开发工程师微服务面试题-进阶版
java·spring cloud·微服务
zkmall15 分钟前
Java + 鸿蒙双引擎:ZKmall开源商城如何定义下一代B2C商城技术标准?
java·开源·harmonyos
陌路物是人非15 分钟前
uniapp取消浏览自动填充
java·服务器·uni-app
獨枭17 分钟前
使用 163 邮箱实现 Spring Boot 邮箱验证码登录
java·spring boot·后端
伍六星17 分钟前
maven和npm区别是什么
java·npm·maven
才知山高路远21 分钟前
Java - Junit框架
java·junit·log4j
维基框架22 分钟前
Spring Boot 封装 MinIO 工具
java·spring boot·后端
秋野酱22 分钟前
基于javaweb的SpringBoot酒店管理系统设计与实现(源码+文档+部署讲解)
java·spring boot·后端
关于不上作者榜就原神启动那件事23 分钟前
Java基础学习
java·开发语言·学习
Echo``28 分钟前
1:OpenCV—图像基础
c++·图像处理·人工智能·opencv·算法·计算机视觉·视觉检测