Java类加载解析阶段的核心是将字节码中与内存无关的符号引用(如类名、方法签名)按需替换为运行时可直接定位目标的直接引用(如内存地址、偏移量),发生在首次执行相关指令(如getstatic、invokestatic)前,而非类加载完成时。

Java 类加载的解析阶段,核心就是把字节码里那些“看得懂但用不了”的符号引用,替换成运行时真正能定位目标的直接引用。这个过程不发生在类刚被读进内存时,而是在第一次真正要用到它的时候才动手——也就是“按需解析”。
符号引用长什么样?
它就安静地躺在 class 文件的常量池里,是纯文本、无地址的描述信息:
-
类名:比如
"java/lang/String"(CONSTANT_Class_info 指向一个 CONSTANT_Utf8_info) -
字段:如
"count:I",由 CONSTANT_Fieldref_info 组合指向类名、字段名和描述符三个 UTF-8 字符串 -
方法:像
"toString()Ljava/lang/String;",通过 CONSTANT_Methodref_info 关联类、方法名和描述符
这些内容在编译后就固定了,不依赖任何内存布局,也不指向真实对象。
解析到底什么时候发生?
不是一加载完就全解析,而是等字节码指令真要动它时才触发。常见触发点包括:
立即学习“Java免费学习笔记(深入)”;
- 执行
getstatic或putstatic访问静态字段前 - 执行
getfield或putfield读写实例字段前 - 调用
invokestatic、invokevirtual、invokeinterface前 - 使用
new创建对象、checkcast类型转换时
也就是说,一个类即使已完成加载、验证、准备,它的字段或方法仍可能处于“符号状态”,直到第一次被访问那一刻,JVM 才去查常量池、找类、算偏移、建引用。
解析具体做哪些事?
以解析一个静态字段为例,JVM 会逐步完成这几步:
- 确认该字段所属的类已加载、验证、准备完毕;若没有,先触发类加载流程
- 在目标类的字段表中,用字段名 + 描述符(如
"value:I")精确匹配 - 检查访问权限(例如 private 字段不能被外部类成功解析)
- 计算该字段在类镜像或对象实例中的内存偏移量(如 +12 字节),这就是直接引用
- 把结果缓存起来,后续相同符号引用直接复用,不再重复解析
接口方法、虚方法等还会额外校验实现关系或是否为 default 方法,但核心逻辑一致:从字符串描述 → 到内存位置。
怎么验证解析是否生效?
可以借助几个轻量方式观察效果:
- 加 JVM 参数
-XX:+TraceClassLoading,配合代码中首次调用getstatic,看日志里类加载与字段访问的时间先后关系 - 故意删掉被引用的类,再运行含
getstatic的代码,抛出NoClassDefFoundError或IncompatibleClassChangeError——说明解析失败发生在使用前,而非加载时 - 用
javap -v查看 class 文件常量池,确认符号引用结构;再结合 JOL(Java Object Layout)工具观察字段实际偏移,对比理解“文本→地址”的落地过程


















