不是,JVM首次解析getstatic时将符号引用转为直接引用并缓存至ConstantPoolCache,后续直接读取缓存的field_offset和oop地址,跳过查找与校验。

getstatic 指令执行时,JVM 真的每次都要查常量池吗?
不是。JVM 在首次解析 getstatic 时会完成符号引用到直接引用的转换,并将结果缓存进 ConstantPoolCache(简称 CP Cache),后续调用直接走缓存——这是 HotSpot VM 实现快速字段访问的关键机制。
CP Cache 是每个类的 InstanceKlass 对象中维护的一块连续内存区域,和常量池(ConstantPool)物理分离,但逻辑上一一映射。它不存储原始符号,而是存放已解析的内存地址、访问权限校验结果、以及指向目标字段或方法的指针。
ConstantPoolCache 的结构如何对应 getstatic 的解析项?
每条 getstatic 指令在字节码中携带一个常量池索引(比如 #15),该索引指向一个 CONSTANT_Fieldref_info 结构。JVM 在链接阶段(首次执行前或初始化时)会:
- 根据
#15查出字段所属类、名称、描述符 - 定位该字段在目标类的
fieldArray中的偏移量 - 验证访问权限(如是否为 public / protected / 同包)
- 将上述结果打包成一个
ConstantPoolCacheEntry,写入 CP Cache 对应槽位(索引相同)
之后每次执行 getstatic,解释器或 C1/C2 编译器都直接读取这个 cache entry 里的 field_offset 和 oop 地址,跳过所有符号查找与权限检查。
为什么 javap -v 看不到 ConstantPoolCache?
javap 只反汇编静态字节码和常量池内容,而 ConstantPoolCache 是运行时由 JVM 动态构建的,属于 InstanceKlass 的原生内存布局,不在 .class 文件里。
想观察它,必须借助 HotSpot 调试手段:
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
- 启动 JVM 时加
-XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly(需 hsdis) - 用
jhsdb jmap --heap或jhsdb clhsdb连接运行中进程,执行inspect查看cp_cache字段 - 在 OpenJDK 源码中搜索
ConstantPoolCache::entry_at和InterpreterMacroAssembler::load_field_from_cp理解其加载逻辑
注意:CP Cache 的 slot 数量默认等于常量池大小,但只有被实际使用的字段/方法引用才会被填充;未使用的 slot 保持为空(0x0)。
final static 字段为何可能绕过 CP Cache?
如果字段同时满足 static + final + 基本类型或 String 字面量(如 public static final int PORT = 8080;),Javac 会将其值内联进调用方的常量池,并生成 ldc 指令而非 getstatic。
此时根本不会触发字段解析,自然也无需 CP Cache。你可以用 javap -v 验证:Code: 段里出现的是 ldc #5,而不是 getstatic Xxx.PORT:I。
这种优化发生在编译期,和运行时的 CP Cache 是两条独立路径——前者消灭指令,后者加速指令。
真正依赖 CP Cache 的,是那些无法在编译期确定值的静态字段,比如 static int count = init(); 或 static final URL BASE = new URL("...");。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










