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