1043 字
约 3 分钟
6
S05-J05 · 类加载与字节码执行
2024-12-31
2026-11-07

适用读者:被"双亲委派""打破双亲委派"绕晕的人。
前置:S04-J03(类文件结构)。
实测环境:OpenJDK 21.0.12.1(类加载器链实测,2026-11-05)。
覆盖旧文:A-1129(515) A-2588(404) A-2583(986) A-2586(587) A-2589(380) + 1009(86) 1015(75) 1031(80) 987(71) 986(65) 1620(126) 1643(301) 1618(189) 1029(83) = 3,948 点。
覆盖扩展:E-S05-013(双亲委派与破坏)、E-S05-014(执行引擎)。
上一篇:S05-J04 · JVM 运行时数据区与垃圾回收基础
下一篇:S05-A01 · GC 收集器选型与调优。


1. 一句话结论

类加载分五步(加载→验证→准备→解析→初始化);双亲委派的意义是"保证核心类不被自定义类覆盖";它被"破坏"的三种方式都是为了插件化/隔离。


2. 实测:类加载器链

ClassLoader cl = Cl.class.getClassLoader();
while (cl != null) { print(cl); cl = cl.getParent(); }

实测输出:

jdk.internal.loader.ClassLoaders$AppClassLoader@639fee48
jdk.internal.loader.ClassLoaders$PlatformClassLoader@2a139a55
String 类加载器: null(null = Bootstrap)
加载器 负责
Bootstrap(null) java.* 核心类(C++ 实现,Java 里显示 null)
Platform(原 Extension) JDK 平台模块
App(原 System) classpath 上的应用类
自定义 插件、热部署

String.class.getClassLoader() == null 就是"核心类由 Bootstrap 加载"的直接证据。


3. 双亲委派与它的三个"破坏"(E-S05-013)

流程:收到加载请求 → 先问父加载器 → 父加载不了才自己加载。

破坏方式 场景
重写 loadClass 热部署(如某些框架每次重新加载)
线程上下文类加载器(TCCL) SPI/JDBC:接口在 Bootstrap,实现在应用 classpath
OSGi/模块化 网状委派而非树状

TCCL 的意义:ServiceLoader 用 Thread.currentThread().getContextClassLoader()——因为"接口由父加载器加载、实现由子加载器加载",父看不到子,只能靠线程上下文反向查找(与 S04-J05 的 SPI 呼应)。


4. 五个阶段与"初始化时机"

阶段 做什么
加载 读字节码 → 生成 Class 对象
验证 格式/语义/字节码校验
准备 静态变量分配内存并赋零值(不是初始值!)
解析 符号引用 → 直接引用
初始化 执行 <clinit>(静态块 + 静态变量赋值)

"准备赋零值"解释了 S04-B04 的初始化顺序:静态变量先 0,再在 <clinit> 里赋真值。

触发初始化的六个时机(记忆点):new、访问静态字段/方法、反射、子类初始化、主类、MethodHandle。


5. 执行引擎(E-S05-014)

方式 说明
解释执行 逐条翻译字节码
JIT 编译(C1/C2) 热点代码编译成机器码
分层编译 先 C1 快速编译,再 C2 深度优化
去优化(Deopt) 假设被打破 → 退回解释执行

这就是"Java 启动慢但跑久了快"的原因:预热期在收集profile,稳定后 C2 编译的代码性能接近 C++。


6. 五个误区

误区 真相
"双亲委派是强制的" 可被重写(三种破坏方式)
"准备阶段就赋初始值" 赋零值,初始值在 <clinit>
"Bootstrap 加载器能拿到" Java 里是 null
"类加载一次就永远不变" 可卸载(需类加载器可回收)
"JIT 编译后不会回退" 可去优化

7. 自检

  1. 五个阶段分别做什么?"准备"赋什么值?
  2. 双亲委派解决什么问题?
  3. 三种"破坏"分别为了什么场景?
  4. TCCL 为什么必须存在(结合 SPI)?
  5. 为什么 Java "启动慢、跑久了快"?

8. 下一篇

S05-A01 · GC 收集器选型与调优

类加载讲完,进架构师层:CMS/G1/ZGC 怎么选,以及 GC 日志里哪个信号最该警惕。


本篇属于系列《S05 · Java 并发与 JVM》。旧文来源:A-1129/2588/2583/2586/2589/1009/1015/1031/987/986/1620/1643/1618/1029;扩展点 E-S05-013/014。类加载器链为 OpenJDK 21 实测。

S05-J05 · 类加载与字节码执行
https://www.clxhxhhr.top/posts/5251/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。