Java类的加载过程

Java类的加载过程是JVM将类的.class文件中的二进制数据读入内存,并进行校验、转换、初始化等一系列复杂操作,最终形成可以被JVM直接使用的Java类型的过程。为了帮助你快速建立整体认知,下面这张图清晰地展示了其核心阶段和关键任务:

下面我们来详细解读每个阶段。

🔎 详解加载过程

1. 装载(Loading)

装载是类加载的第一个阶段,主要完成三件事:

  • 获取二进制字节流 :通过类的全限定名(如 java.lang.String)来获取定义此类的二进制字节流。JVM规范并未限制字节流的来源,它可以从ZIP/JAR/WAR包、网络、运行时计算生成(动态代理)或其它任何地方获取 。
  • 转换静态结构 :将这个字节流所代表的静态存储结构 (Class文件格式)转化为方法区(Metaspace)的运行时数据结构
  • 创建Class对象 :在内存中(Java堆)生成一个代表这个类的 java.lang.Class对象,作为程序访问方法区中这些数据结构的入口 。

2. 链接(Linking)

链接阶段负责将加载到内存的类数据合并到JVM运行时环境中,可分为三个子阶段:

  • 验证(Verification)​​:确保Class文件的字节流包含的信息符合当前虚拟机的要求,不会危害虚拟机自身安全。主要包括 :

    • 文件格式验证 :验证魔数(是否以0xCAFEBABE开头)、主次版本号等 。
    • 元数据验证:对字节码描述的信息进行语义分析,如是否有父类、是否继承了不允许继承的类(final类)等 。
    • 字节码验证:通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的 。
    • 符号引用验证:发生在解析阶段,确保解析动作能正确执行 。
  • 准备(Preparation)​ ​:为类变量(static变量)​ 分配内存,并将其初始化为默认零值 ​(如int为0,boolean为false,引用类型为null)。注意,这里是数据类型的零值,而非代码中显式赋予的值(public static int value = 123;的变量value在准备阶段后初始值为0,赋值为123的动作在初始化阶段执行)。

  • 解析(Resolution)​ ​:虚拟机将常量池内的符号引用 替换为直接引用的过程 。符号引用用一组符号来描述所引用的目标,与虚拟机内存布局无关;直接引用可以是直接指向目标的指针、相对偏移量或能间接定位到目标的句柄,与虚拟机内存布局相关 。

3. 初始化(Initialization)

这是类加载过程的最后一步,真正开始执行类中定义的Java程序代码(字节码)。在此阶段,JVM会执行类构造器 <clinit>()方法 ,该方法由编译器自动收集类中的所有类变量的赋值动作静态语句块(static{}块)​ 中的语句合并生成 。虚拟机会保证在子类的 <clinit>()方法执行前,父类的 <clinit>()方法已经执行完毕 。

⏰ 类初始化时机

JVM规范严格规定了有且只有以下6种情况必须立即对类进行"初始化"(而加载、验证、准备自然需要在此之前开始):

  1. 创建类的实例 :如 new MyClass()
  2. 访问或赋值静态变量/字段:访问某个类或接口的静态变量,或者对该静态变量赋值(被final修饰、已在编译期把结果放入常量池的静态字段除外)。
  3. 调用类的静态方法
  4. 使用反射 :如 Class.forName("com.example.MyClass")
  5. 初始化子类:当初始化一个类时,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。
  6. 虚拟机启动时指定的主类:当虚拟机启动时,用户需要指定一个包含main方法的主类,虚拟机会先初始化这个主类。

🧠 掌握类加载器

类加载器是实现"通过一个类的全限定名来获取描述该类的二进制字节流"这个动作的代码模块 。JVM提供了三层类加载器,遵循双亲委派模型​ :

加载器类型 职责说明 父加载器 备注
启动类加载器 (Bootstrap ClassLoader)​ 加载 JAVA_HOME/lib下的核心库(如rt.jar) 无(由C++实现,在Java中表现为null ) 最顶层,不继承java.lang.ClassLoader
扩展类加载器 (Extension ClassLoader)​ 加载 JAVA_HOME/lib/ext目录下的扩展库 Bootstrap ClassLoader sun.misc.Launcher$ExtClassLoader实现
应用程序类加载器 (Application ClassLoader)​ 加载用户类路径(CLASSPATH)上的类库 Extension ClassLoader 也称系统类加载器,是程序的默认类加载器

双亲委派模型的工作过程 是:当一个类加载器收到类加载请求时,它首先不会自己去尝试加载,而是将这个请求委派给父类加载器去完成。每一层加载器都是如此,只有当所有父加载器都无法完成加载请求(在自己的搜索范围内找不到所需的类)时,子加载器才会尝试自己去加载 。这种模型的好处是:

  • 避免类的重复加载,确保一个类在JVM中具有唯一性 。
  • 保证Java核心API的安全 ,防止核心库被随意篡改(例如,用户自定义的java.lang.Object类不会被加载,因为会由顶层的Bootstrap ClassLoader加载核心的Object类)。

💎 总结

理解Java类的加载过程,对于深入理解JVM工作原理、诊断类加载相关问题(如ClassNotFoundException, NoClassDefFoundError)、实现热部署或进行高级框架设计都至关重要。其核心在于装载、链接、初始化 三个阶段,以及由双亲委派模型组织的类加载器体系。

相关推荐
间彧3 小时前
Java双亲委派模型的具体实现原理是什么?
后端
DokiDoki之父3 小时前
Spring—注解开发
java·后端·spring
提笔了无痕4 小时前
什么是Redis的缓存问题,以及如何解决
数据库·redis·后端·缓存·mybatis
浪里行舟4 小时前
国产OCR双雄对决?PaddleOCR-VL与DeepSeek-OCR全面解析
前端·后端
lang201509284 小时前
Spring Boot缓存机制全解析
spring boot·后端·缓存
桦说编程5 小时前
Java并发编程:两种控制并发度的实现方法及其比较
java·后端
Moment5 小时前
性能狂飙!Next.js 16 重磅发布:Turbopack 稳定、编译提速 10 倍!🚀🚀🚀
前端·javascript·后端
lecepin6 小时前
AI Coding 资讯 2025-10-22
前端·javascript·后端