news 2026/7/7 12:30:15

【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/30 2:12:06

吐血推荐MBA必用TOP8一键生成论文工具

吐血推荐MBA必用TOP8一键生成论文工具 2026年MBA论文写作工具测评&#xff1a;为何需要一份精准推荐&#xff1f; MBA学习过程中&#xff0c;论文撰写是必不可少的一环&#xff0c;但面对繁杂的文献资料、严格的格式要求以及紧迫的时间节点&#xff0c;许多学生常常陷入效率低…

作者头像 李华
网站建设 2026/6/26 15:37:04

仅限内部分享:高并发系统中PHP跨域请求的3大优化策略

第一章&#xff1a;PHP跨域请求处理的核心挑战在现代Web开发中&#xff0c;前端与后端分离架构日益普及&#xff0c;PHP作为常见的服务端语言&#xff0c;常面临浏览器同源策略带来的跨域请求问题。当客户端发起的HTTP请求目标与当前页面协议、域名或端口任一不同时&#xff0c…

作者头像 李华
网站建设 2026/6/26 15:37:09

AES加密传输在SpringBoot大文件上传中的实际应用

大文件传输系统建设方案&#xff08;技术方案及部分代码示例&#xff09; 一、项目背景与需求分析 作为集团数字化转型重点项目&#xff0c;需构建支持100GB级文件传输、全信创环境兼容、军工级安全加密的分布式文件传输系统。核心需求包括&#xff1a; 性能要求&#xff1a…

作者头像 李华
网站建设 2026/6/30 14:22:59

如何实现PHP服务0宕机?(构建智能监控与自动告警系统的秘密武器)

第一章&#xff1a;PHP服务0宕机的核心理念实现PHP服务的0宕机运行&#xff0c;核心在于构建高可用、可热更新和自动容错的系统架构。这不仅依赖于代码质量&#xff0c;更需要从部署策略、进程管理与资源调度等多维度协同设计。无感重启机制 通过使用Swoole等常驻内存框架&…

作者头像 李华
网站建设 2026/6/25 23:59:28

一文讲清 RAID 常见故障类型、失效机制

很多人对 RAID 有一种天然的误解: “只要上了 RAID,就等于数据安全了。” 这句话,在生产环境里,危险系数非常高。 我见过的实际案例中,RAID 出问题往往不是“技术不够先进”,而是对 RAID 故障形态理解不够。 RAID 故障,本质上就三大类 不管你用的是 RAID 0、1、5、6…

作者头像 李华
网站建设 2026/7/1 21:26:31

PHP错误日志没人看?用这4步打造主动式告警系统,故障提前15分钟发现

第一章&#xff1a;PHP错误日志没人看&#xff1f;被动运维的代价与破局思路在现代Web开发中&#xff0c;PHP依然占据着重要地位&#xff0c;但许多团队对错误日志的处理仍停留在“出问题再查”的被动模式。这种运维方式往往导致小问题演变为系统性故障&#xff0c;例如未捕获的…

作者头像 李华