JVM 类加载与字节码概览
类如何被加载、链接与初始化,以及字节码作为执行中间表示的边界(规范 vs 实现)。
#type / concept
#status / growing
#tech / dev
#resource / java
[!info] 关联笔记
JVM 类加载与字节码概览
这个概念为什么出现
ClassNotFoundException、NoClassDefFoundError、版本不匹配、SPI 与热加载问题,最终都落到类如何进入 JVM。字节码是编译产物的可验证中间表示。
[!abstract] 一句话理解 类加载大致经历加载→链接(验证/准备/解析)→初始化;字节码由类文件承载,JVM 解释/JIT 执行;不同加载器形成命名空间。
最小可观察示例
场景:确认应用类由哪个加载器装入
public class LoaderPeek {
public static void main(String[] args) {
ClassLoader cl = LoaderPeek.class.getClassLoader();
System.out.println(cl);
System.out.println(String.class.getClassLoader()); // 引导/平台路径,常显示 null
}
}
javac LoaderPeek.java && java LoaderPeek
期望:应用类加载器非空字符串描述;String 的加载器打印为 null(引导加载器历史表示)。
结合场景再看三个关注点
- 类身份 = 全名 + 加载器。
- 初始化触发点(首次主动使用)影响 static 副作用。
- 字节码版本与运行 JDK 要兼容。
核心概念与准确模型
1. 加载器层级(经典模型,模块时代有演进)
- 引导 / 平台 / 应用
- 委托机制避免核心类被替换
2. 链接与初始化
- 验证字节码安全
- 准备静态字段内存
- 初始化执行
<clinit>
3. 字节码
javap -c观察- 操作数栈与局部变量表模型
边界情况与反直觉行为
- 异常
ClassNotFoundExceptionvsNoClassDefFoundError语义不同。 - 循环初始化可能看到默认值。
- 容器/插件多加载器极易“同名类转换失败”。
常见误区
[!warning] 常见误区:以为类只加载一次全局唯一 不同加载器可加载同名类为不同运行时类。
工程实践
- 依赖冲突用 jdeps/构建 tree 先查。
- 框架 SPI 注意
ServiceLoader与 TCCL。 - 生产开启类加载相关诊断要克制开销。
- 读 JVMS 章节时区分必须 vs 实现。
本节总结
- 加载链路解释大量“找不到类”
- 加载器命名空间
- 字节码是可验证 IR
自测题
- 为什么容器里 ClassCastException 可能“看起来类型一样”?
- static 初始化何时跑?
参考答案
- 同类名由不同加载器加载,运行时类型不同。
- 类型首次主动使用触发初始化(规范定义的触发条件)。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| JVMS Class Loading | 规范 | 加载与链接 |
| javap tool | 工具 | 反汇编 |