类加载过程之加载的详细理解

根据《Java虚拟机规范》,加载阶段(Loading)的核心任务是将类的二进制数据加载到方法区中,并创建对应的 Class 对象。以下是对加载阶段的详细解析:

  1. 加载阶段的核心操作
  • 获取二进制字节流:

  • 从 .class 文件、JAR包、网络资源、动态生成(如字节码框架)等来源读取类的二进制数据。

  • 示例:通过类加载器( ClassLoader )的 findClass 方法获取字节流。

  • 转换为方法区数据结构:

  • 将字节流解析为方法区中的内部格式,包括:

  • 类的元数据(全限定名、父类、接口、访问修饰符等)。

  • 字段和方法的描述符(如 Ljava/lang/String; 表示字符串类型)。

  • 运行时常量池(存储字面量和符号引用)。

  • 创建 Class 对象:

  • 在堆内存中生成 java.lang.Class 类的实例,作为访问方法区数据的入口。

  1. 加载阶段的特点
  • 不验证合法性:

  • 加载阶段仅负责读取和存储数据,不检查字节流的格式(如魔数、版本号)。这些验证属于链接阶段的验证子阶段。

  • 类加载器的多样性:

  • 不同的类加载器(如启动类加载器、扩展类加载器、应用类加载器)负责加载不同路径的类。

  • 示例:启动类加载器加载 java.lang 包的类,应用类加载器加载用户自定义类。

  • 延迟加载:

  • 类加载可能在首次使用时触发(如通过反射、实例化对象),而非预先加载所有类。

  1. 方法区存储的内容

加载阶段完成后,方法区存储以下数据:

  • 类的元数据:
  • 类的全限定名、直接父类、实现的接口列表。
  • 字段信息(名称、类型、修饰符)和方法信息(名称、描述符、修饰符)。
  • 运行时常量池:
  • 字面量(如字符串常量、基本类型值)。
  • 符号引用(类、字段、方法的符号名称)。
  • 静态变量:
  • 在链接阶段的准备子阶段分配内存并设置初始值(零值)。
  1. 加载阶段的代码示例

// 自定义类加载器(简化版)
public class MyClassLoader extends ClassLoader {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// 1. 读取类的二进制字节流(假设从文件或网络获取)
byte[] classBytes = loadClassBytes(name);

    // 2. 将字节流转换为方法区数据,并创建Class对象
    return defineClass(name, classBytes, 0, classBytes.length);
}

private byte[] loadClassBytes(String name) {
    // 实际实现需从数据源读取字节流
    // 加载阶段不验证字节流合法性,即使魔数错误也会继续
    return new byte[0]; 
}

}

// 使用自定义类加载器加载类
MyClassLoader loader = new MyClassLoader();
Class<?> clazz = loader.loadClass("com.example.MyClass");
// 此时类已加载到方法区,但未验证(验证在链接阶段执行)

  1. 加载阶段与其他阶段的关系
  • 链接阶段:
  • 验证:检查字节流的合法性(如魔数、版本号)。
  • 准备:为静态变量分配内存并设零值。
  • 解析:将符号引用转换为直接引用(可选)。
  • 初始化阶段:
  • 执行静态变量赋值和静态代码块。
  1. 总结
  • 加载阶段的核心:将类的二进制数据加载到方法区,并创建 Class 对象。
  • 阶段职责边界:
  • 加载阶段:数据获取与存储(不验证)。
  • 链接阶段:合法性验证、内存分配、符号解析。
  • 初始化阶段:执行类构造器 () 。

理解加载阶段的机制有助于掌握类加载的时序,并在开发中合理利用类加载器(如热部署、字节码增强)。

posted @   lllrrrqqq  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示