JVM规范第 5 章:加载、链接与初始化
基于 Oracle 官方《The Java Virtual Machine Specification》第 5 章Java SE 26编写。这是什么为什么值得懂编译期产出的只是一堆字节。真正让new Foo()能工作、让父类静态变量先就绪、让NoClassDefFoundError这类异常有据可查的是第 5 章定义的生命周期三阶段加载Loading找到并读取类的二进制表示据此创建这个类/接口。链接Linking把类并入 JVM 运行时使其可被调用含验证、准备、解析。初始化Initialization执行类/接口的clinit方法真正跑你写的静态代码。这三个词常被混为一谈但它们在规范里是严格区分、顺序约束的三个阶段。读懂本章你能解释为什么父类一定先初始化为什么static final int在「准备」阶段有默认值、而显式赋值却在「初始化」阶段才发生ClassNotFoundException、NoClassDefFoundError、VerifyError、LinkageError各自在哪一阶段抛出那些「诡异的第一次访问才触发」行为懒加载从何而来。1. 总览三阶段与运行时结构JVM 是动态地加载、链接、初始化类和接口的——不是一次性把所有类都准备好而是按需发生。┌─────────────┐ 磁盘 │ class 文件 │ └──────┬──────┘ │ ① 加载 Loading ▼ ┌─────────────┐ │ 内存中创建类 │ 方法区构造内部表示运行时常量池建成 └──────┬──────┘ │ ② 链接 Linking │ ├─ 验证 Verification │ ├─ 准备 Preparation │ └─ 解析 Resolution (可延迟) ▼ ┌─────────────┐ │ ③ 初始化 │ 执行 clinit └─────────────┘1.1 运行时常量池每个类/接口都有运行时常量池由class文件里的constant_pool在创建时构造。池里两类条目符号引用symbolic references后续可能被「解析」§5.4.3成直接引用。如CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info、CONSTANT_MethodHandle/Type、CONSTANT_Dynamic、CONSTANT_InvokeDynamic。静态常量static constants无需进一步处理。CONSTANT_String_info经String.intern处理、数值常量按 IEEE 754。「可加载loadable」条目可被ldc系列指令压栈或作引导方法参数类/接口符号引用、方法句柄、方法类型、动态计算常量、静态常量。2. 加载2.1 核心概念定义加载器defining loader真正把类定义下来调用defineClass的加载器L。初始加载器initiating loader发起加载可能委托出去的加载器。一个类由二进制名 定义加载器唯一确定归属单一运行时包。类加载器分两类启动类加载器bootstrap与用户定义加载器ClassLoader子类。2.2 三种加载路径由类 D 的运行时常量池符号引用名 N 触发时N 是非数组类/接口且 D 由 bootstrap 定义→ 用 bootstrap 初始加载N 是非数组类/接口且 D 由用户加载器定义→ 同用户加载器初始加载N 是数组类→ JVM 直接创建数组类关联 D 的定义加载器。数组类的组件若是引用类型会递归加载组件类型数组类的定义加载器记为组件类的定义加载器否则记为 bootstrap。2.3 从 class 文件派生类这是最关键的「格式检查」步骤任何失败都会抛出对应Error权限/循环检查已在初始加载中 →LinkageError循环派生 →ClassCircularityError。解析二进制不是合法ClassFile→ClassFormatError版本不支持 →UnsupportedClassVersionError类名与实际不符 →NoClassDefFoundError。解析父类父类是接口或final→IncompatibleClassChangeError违反PermittedSubclasses密封类限制 → 同错。解析超接口超接口非接口 →IncompatibleClassChangeError同理检查许可子类。全部通过才标记 L 为定义加载器、记录初始加载器并在方法区创建内部表示。这也是为什么你之前看到的UnsupportedClassVersionError: major version 65实实在在发生在「加载」这一步——它根本还没到链接/运行。2.4 加载约束为保证类型安全当类CN1,L1引用DN2,L2的字段/方法时描述符中的类型名在 L1、L2 下必须是同一个类。约束形如NL1 NL2在准备/解析时强加。若两加载器都记录了初始加载 N、约束暗示相等、但实际加载出不同的类 → 撤销记录并抛LinkageError。2.5 模块与层类按运行时包归属某个运行时模块从而控制跨模块访问§5.4.4。defineModules创建模块并绑定类加载器「层layer」含 boot layer 与用户层。未命名模块unnamed module可读所有模块、导出所有包——这解释了为什么 classpath非模块路径上的代码几乎不受模块限制。3. 链接Linking链接可能抛OutOfMemoryError分配内存失败。它包含三步不必同时完成链接的硬性顺序约束完全加载后才链接完全验证、准备后才初始化链接错误在程序触发点抛出不是提前动态计算常量/invokedynamic调用点延迟解析。3.1 验证Verification确保二进制满足结构约束第 4 章 §4.9 的验证规则。不满足静态/结构约束 →VerifyError。因LinkageError失败的后续验证会重复同一错误。3.2 准备Preparation为静态字段分配空间并赋默认值第 2 章 §2.3/2.4不执行任何 Java 代码。例如publicstaticintx10;publicstaticbooleanflagtrue;在准备阶段x被设为0、flag被设为false——你写的 10/ true此时还没执行。同时强加加载约束重写方法的超类/超接口描述符类型需一致。准备必须在初始化之前完成。3.3 解析Resolution把符号引用替换为直接引用指针/偏移等。多条指令依赖解析getfield、invokevirtual、ldc等。解析类型与查找规则解析目标规则要点失败异常类/接口用定义加载器加载 N数组递归解析元素最后做访问控制NoClassDefFoundError等字段先解析所属类 C按 C → 超接口 → 父类查找字段NoSuchFieldError方法C 是接口 →IncompatibleClassChangeError查 C 及父类、超接口NoSuchMethodError接口方法C 非接口 →IncompatibleClassChangeError类似查找NoSuchMethodError方法类型/句柄方法类型解析其描述符中的类方法句柄按 9 种行为表 5.4.3.5-A解析见规范动态引导方法句柄 静态参数 → 调用 → 验证—字段/方法解析失败抛出的NoSuchFieldError/NoSuchMethodError都是LinkageError的子类——这类错误通常在首次主动使用时才暴露。3.4 访问控制Access Control解析后还要检查访问权限类 C 对 D 可访问同模块且 public、跨模块且被读且导出、或同包非 public字段/方法 R 对 D 可访问public、protected且 D 是子类、同包默认、或同 nest 的 private。失败 →IllegalAccessError同样属LinkageError家族。4. 初始化Initialization初始化 执行类/接口的clinit方法。它发生在加载、链接验证准备之后。显式静态字段赋值就是在这里跑的——这正是「准备阶段只给默认值」的补全。4.1 何时触发初始化6 类主动使用按规范类/接口在其首次主动使用时初始化。触发条件包括创建类的实例new调用类的静态方法使用或赋值类的静态字段非常量字段static final编译期常量除外见 §4.3调用类/接口的反射方法如Class.forName初始化其子类时父类必须先初始化父类优先JVM 启动时被指定为初始类含main方法的那个。注意static final基本类型/String 常量带ConstantValue属性在编译期就内联进使用处不触发定义类的初始化——这就是「常量不触发类加载」现象的根因。4.2clinit语义与死锁clinit由 JVM 保证线程安全多线程同时首次使用同一类只有一个线程执行clinit其余阻塞等待。但若clinit中又去主动使用另一个正在初始化的类可能造成死锁规范明确提示需警惕循环初始化依赖。接口初始化较宽松接口不要求其超接口先初始化除非用到超接口的非常量字段。初始化失败会抛ExceptionInInitializerError且该类后续再被使用会抛NoClassDefFoundError。5. 启动、绑定与终止启动JVM 通过 bootstrap 或用户加载器创建初始类链接、初始化它创建实例如需调用 main。初始类由实现相关方式指定通常命令行参数。绑定本地方法native方法在首次调用时把本地实现绑定到方法详见System.loadLibrary/ JNI 机制。终止JVM 在所有非守护线程结束后方可退出System.exit可主动终止。6. 一张「异常在哪个阶段抛出」速查表异常阶段含义ClassNotFoundException加载用户加载器找不到loadClass返回 null/未找到NoClassDefFoundError加载/解析链接时找不到类定义ClassFormatError加载派生不是合法ClassFileUnsupportedClassVersionError加载派生主版本不被支持ClassCircularityError加载派生类继承出现循环IncompatibleClassChangeError加载/解析父类是接口/final、密封类冲突等VerifyError链接验证字节码不满足约束NoSuchFieldError/NoSuchMethodError链接解析字段/方法不存在IllegalAccessError链接访问控制无访问权限LinkageError多阶段上述链接错误的父类ExceptionInInitializerError初始化clinit抛异常OutOfMemoryError链接分配内存不足7. 动手用-verbose:class观察加载顺序写两个有继承关系的类// Parent.javapublicclassParent{static{System.out.println(Parent clinit);}}// Child.javapublicclassChildextendsParent{static{System.out.println(Child clinit);}publicstaticvoidmain(String[]a){System.out.println(main runs);}}运行并打开类加载日志javac Parent.java Child.javajava-verbose:classChild你会看到先Parent后Child的clinit输出父类优先初始化以及 JVM 启动时大量 bootstrap 类的加载记录。再用Class.forName(Parent)对比「反射触发主动使用」体会懒加载的触发点差异。小结加载找字节、派生类、建内部表示失败抛ClassFormatError/UnsupportedClassVersionError等链接验证结构、准备静态字段默认值、解析符号→直接引用、访问控制初始化执行clinit首次主动使用时触发父类优先线程安全但警惕死锁大量LinkageError子类在对应阶段抛出是排错的关键坐标。

相关新闻

LLMVulExp:LLM 驱动的可解释漏洞检测

LLMVulExp:LLM 驱动的可解释漏洞检测

软件漏洞是现代计算系统的重大威胁——一旦被利用,轻则隐私泄露,重则系统沦陷。然而,现有漏洞检测方法(深度学习 / 预训练模型)只告诉你"有漏洞"或"无漏洞",不告诉你漏洞在哪、为什么有…

2026/10/28 11:40:13 阅读更多 →
大模型怎么“看懂”你的业务数据?

大模型怎么“看懂”你的业务数据?

大模型怎么“看懂”你的业务数据?关键词:对话式数据分析、自然语言转 SQL、企业数据库、数据图表、AI 智能体、成都硅基边界 适合人群:企业技术 / 数据 / 运营,想让 AI 直接查业务库、生成报表、自动录单的团队。 系列&#xff1a…

2026/10/25 7:43:02 阅读更多 →
瑞安汽摩配独立站建站:模板站还是定制站?别再花冤枉钱

瑞安汽摩配独立站建站:模板站还是定制站?别再花冤枉钱

瑞安汽摩配工厂外贸独立站建设:模板站与定制站对比分析 📌 概述:对于瑞安汽摩配工厂的外贸独立站建设,模板站与定制站各有适用场景,选择取决于工厂的当前阶段、预算和长期规划。本文从汽配行业的产品特性、建站成本、运…

2026/10/23 2:37:03 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/10/27 17:33:32 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/10/28 7:28:00 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/10/28 9:02:11 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/10/27 18:20:50 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/10/28 9:00:54 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/10/28 7:29:02 阅读更多 →

日新闻

周新闻

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/27 13:55:19 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/28 9:01:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/27 19:19:37 阅读更多 →