news 2026/6/12 19:58:33

【JVM】类初始化和加载

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【JVM】类初始化和加载

一、对象的创建过程

1. 类加载检查

当尝试创建一个对象时,JVM首先会到元空间中查找该对象的类符号引用(可以理解为类的模板信息)。检查这个类是否已经被加载、解析和初始化。如果没有,则会触发完整的类加载过程

2. 分配内存

类加载检查通过后,JVM会在堆内存中为新对象划分一块内存空间。具体的内存分配方式取决于垃圾收集器的实现,常见的有:

  • 指针碰撞:适用于内存规整的GC算法(如Serial、ParNew)
  • 空闲列表:适用于内存不规整的GC算法(如CMS)
3. 初始化零值

内存分配完成后,JVM会将对象的所有实例字段初始化为对应类型的零值

  • 数值类型(int、long等):0
  • 布尔类型:false
  • 引用类型:null

此操作确保了对象字段在使用前都有确定的初始值,这是Java内存安全的重要保障。

4. 设置对象头

接下来设置对象的对象头(Object Header),主要包括:

  • Mark Word:存储对象的运行时数据
    • 哈希码(HashCode)
    • GC分代年龄
    • 锁状态标志
    • 线程持有的锁等
  • Klass Pointer:指向方法区中的Class对象,确定对象的类型
  • 如果是数组对象,还会包含数组长度信息
5. 执行init方法

上述步骤完成后,从JVM角度看一个对象已经创建完毕。接着执行实例初始化

  1. 初始化字段:按照代码顺序执行字段的显式初始化
  2. 执行实例代码块:编译器会将所有非静态代码块收集并插入到构造器开头
  3. 执行构造器代码:执行开发者编写的构造函数代码

至此,一个完整的、可用的Java对象才真正创建完成。


阶段执行内容涉及内存区域
类加载检查查找类符号引用,必要时加载类元空间
分配内存在堆中划分对象空间
初始化零值设置字段默认值(0、false、null)
设置对象头设置对象元数据(Mark Word、类型指针)
执行init方法执行代码块和构造器,完成对象初始化

二、类加载过程

类加载是JVM将类的字节码文件加载到内存,并进行验证、准备、解析和初始化的完整过程。整个过程分为三大阶段:加载 → 链接 → 初始化,其中链接阶段又包含验证、准备、解析三个子阶段。

1. 加载(Loading)

核心任务:将类的字节码数据加载到内存中,并创建对应的Class对象。

具体过程

  • 读取字节码:通过类的全限定名获取对应的字节码文件(.class
  • 二进制转换:将字节码转换为方法区中的运行时数据结构
  • 创建Class对象:在中生成一个代表该类的java.lang.Class对象
  • 建立访问入口:Class对象作为程序访问方法区中类元数据的入口
2. 链接(Linking)

链接阶段负责将加载到内存的类数据进行整合和准备。

2.1 验证(Verification)

确保被加载的类符合JVM规范,不会危害虚拟机安全。

四个验证阶段

验证阶段验证内容重要性
文件格式验证魔数、版本号、常量池等确保.class文件格式正确
元数据验证类的继承关系、字段/方法访问性确保语义符合Java规范
字节码验证方法体、栈帧、类型转换等确保程序逻辑正确性
符号引用验证符号引用的合法性确保解析能正常进行
2.2 准备(Preparation)

为类的静态变量分配内存并设置初始值。

关键规则

// 情况1:普通static变量(赋默认值)publicstaticintcount;// 准备阶段:count = 0publicstaticStringname;// 准备阶段:name = null// 情况2:static final编译时常量(直接赋实际值)publicstaticfinalintMAX=100;// 准备阶段:MAX = 100publicstaticfinalStringTITLE="App";// 准备阶段:TITLE = "App"引用// 情况3:static final非编译时常量(赋默认值)publicstaticfinalDateNOW=newDate();// 准备阶段:NOW = null

内存分配

  • 静态变量在方法区(元空间)分配内存
  • 基本类型分配固定大小,引用类型分配引用空间
2.3 解析(Resolution)

将常量池中的符号引用替换为直接引用

引用类型符号引用(解析前)直接引用(解析后)
类/接口java/lang/StringClass对象内存地址
字段java/lang/System.out字段内存偏移量
方法java/io/PrintStream.println方法入口地址或方法表索引

解析时机

  • 积极解析:类加载时就解析所有符号引用(默认)
  • 惰性解析:第一次使用时才解析(某些JVM实现)
3. 初始化(Initialization)

类加载的最后阶段,执行类的初始化代码。

触发时机(以下情况之一):

  1. 创建类的实例(new
  2. 访问类的静态变量(非final)
  3. 调用类的静态方法
  4. 使用反射(Class.forName()
  5. 初始化子类时,父类未初始化
  6. JVM启动时指定的主类

执行内容:执行<clinit>()方法(类构造器)

publicclassExample{// 静态变量赋值(收集到<clinit>())publicstaticinta=initA();// 静态代码块(收集到<clinit>())static{System.out.println("静态代码块");}privatestaticintinitA(){return100;}// <clinit>()方法包含:// 1. a = initA()// 2. System.out.println("静态代码块")}

初始化顺序

  1. 父类静态变量和静态代码块
  2. 子类静态变量和静态代码块
  3. 父类实例变量和实例代码块
  4. 父类构造器
  5. 子类实例变量和实例代码块
  6. 子类构造器
4. 使用(Using)

类完成加载后进入使用阶段,程序可以:

  • 创建类的实例对象
  • 访问类的静态成员
  • 调用类的方法
  • 使用反射操作类
5. 卸载(Unloading)

当类不再被需要时,JVM会将其从内存中卸载。

卸载条件(同时满足):

  1. 实例全部回收:该类所有的实例都已被垃圾回收
  2. ClassLoader回收:加载该类的ClassLoader已被回收
  3. Class对象无引用:该类的Class对象没有被任何地方引用

卸载过程

// 假设场景:Web应用卸载WebappClassLoaderloader=newWebappClassLoader();Class<?>clazz=loader.loadClass("com.example.MyServlet");// 当Web应用停止时:// 1. 销毁所有MyServlet实例 → 实例回收 ✓// 2. 回收WebappClassLoader → ClassLoader回收 ✓// 3. 清除对Class对象的引用 → 无引用 ✓// 结果:MyServlet类被卸载

注意事项

  1. 启动类加载器加载的类通常不会被卸载
  2. 自定义类加载器加载的类在适当条件下可被卸载
  3. 类的卸载有助于减少元空间内存占用

📌 完整类加载流程图

开始 ↓ [加载阶段] ├── 读取.class文件 ├── 转换运行时结构 ├── 创建Class对象 └── 建立方法区访问入口 ↓ [链接阶段] ├── [验证] │ ├── 文件格式验证 │ ├── 元数据验证 │ ├── 字节码验证 │ └── 符号引用验证 │ ├── [准备] │ ├── 分配静态变量内存 │ ├── 设置默认值/实际值 │ └── 处理static final常量 │ └── [解析] ├── 类/接口解析 ├── 字段解析 └── 方法解析 ↓ [初始化阶段] ├── 执行<clinit>()方法 ├── 初始化静态变量 └── 执行静态代码块 ↓ [使用阶段] ├── 创建实例 ├── 访问成员 └── 调用方法 ↓ [卸载阶段](条件满足时) ├── 回收所有实例 ├── 回收ClassLoader └── 清除Class引用 ↓ 结束

关键要点总结

  1. 类加载是懒加载的:只有真正使用时才会触发初始化
  2. 准备阶段只处理静态变量:实例变量在对象创建时初始化
  3. 解析将符号变直接:从描述性引用到具体内存地址
  4. 初始化执行clinit():收集所有静态初始化代码
  5. 卸载条件苛刻:需要实例、ClassLoader、Class对象都符合条件
  6. 双亲委派保护核心:防止核心类被篡改,确保类唯一性
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 13:07:34

兆易创新存储产品:HeyGem数字人讲解Flash应用场景

兆易创新存储产品在HeyGem数字人系统中的实践应用 在AI驱动内容生成日益普及的今天&#xff0c;如何高效、稳定地运行数字人视频合成系统&#xff0c;成为边缘计算场景下的一项关键挑战。以HeyGem为代表的音视频融合平台&#xff0c;正被广泛用于智能客服、虚拟主播和自动化教育…

作者头像 李华
网站建设 2026/6/10 13:13:59

2026专科生必看!8个降AI率工具测评榜单

2026专科生必看&#xff01;8个降AI率工具测评榜单 为什么专科生需要关注降AI率工具&#xff1f; 随着高校对学术原创性的要求不断提高&#xff0c;AI生成内容检测系统日益严格&#xff0c;许多专科生在撰写论文或报告时都面临“AI率过高”的问题。尤其是在2026年&#xff0c;各…

作者头像 李华
网站建设 2026/6/10 13:13:44

服务注册与发现如何实现?PHP微服务集群稳定性提升80%的秘密

第一章&#xff1a;服务注册与发现如何实现&#xff1f;PHP微服务集群稳定性提升80%的秘密在构建高可用的PHP微服务架构时&#xff0c;服务注册与发现是保障集群稳定性的核心机制。通过动态管理服务实例的生命周期&#xff0c;系统能够在节点故障或扩容时自动调整流量路由&…

作者头像 李华
网站建设 2026/6/10 13:14:23

曼卡龙轻奢路线:HeyGem生成年轻消费者画像动画

曼卡龙轻奢路线&#xff1a;HeyGem生成年轻消费者画像动画 在Z世代成为消费主力的今天&#xff0c;品牌如何用“看得见的声音”讲好自己的故事&#xff1f;传统的广告片拍摄周期长、成本高&#xff0c;一条15秒短视频动辄数万元投入&#xff1b;而社交媒体又要求内容高频更新、…

作者头像 李华
网站建设 2026/6/12 10:37:59

园城黄金金融衍生品:HeyGem生成黄金ETF投资策略讲解

园城黄金金融衍生品&#xff1a;HeyGem生成黄金ETF投资策略讲解 在金融信息传播日益高频、精准度要求不断提升的今天&#xff0c;投资者教育内容的生产效率正面临前所未有的挑战。以黄金ETF这类专业性较强的金融产品为例&#xff0c;如何将复杂的市场趋势和配置逻辑&#xff0…

作者头像 李华
网站建设 2026/6/10 11:37:14

PHP 8.7新函数实战解析:为什么顶尖工程师都在抢着用?

第一章&#xff1a;PHP 8.7新函数概述PHP 8.7 作为 PHP 语言演进中的重要版本&#xff0c;引入了一系列实用的新内置函数&#xff0c;旨在提升开发效率、增强类型安全并简化常见编程任务。这些函数覆盖了字符串处理、数组操作、类型检查和异步支持等多个方面&#xff0c;为现代…

作者头像 李华