ldc指令的核心作用是将运行时常量池中已解析的字面量(如int、float、String)或类引用等,按类型安全地推入操作数栈顶部;它通过8位索引定位常量池项,依据tag类型(如CONSTANT_Integer_info、CONSTANT_String_info)提取对应值并严格类型压栈,不执行计算或隐式转换,long/double需用ldc2_w,且支持ldc_w、ldc2_w等变体以扩展索引范围和数据类型覆盖。

ldc 指令的核心作用,是把运行时常量池中已解析的字面量(如整数、浮点数、字符串)或类引用等,按类型安全地推入当前方法的操作数栈顶部。它不执行计算,也不做隐式类型转换,而是严格依据常量池项的实际类型完成“取值—压栈”动作。
ldc 如何定位并读取常量池内容
ldc 后跟一个 8 位无符号索引(index),该索引指向当前类的运行时常量池中某一项。JVM 在类加载阶段已将 class 文件中的常量池结构载入内存,并完成部分解析(如 UTF-8 字符串转 Java String 对象、类符号引用转 Class 实例等)。执行 ldc 时,虚拟机直接根据 index 查找对应项,检查其 tag 类型(如 CONSTANT_Integer_info、CONSTANT_String_info、CONSTANT_Class_info),再提取其运行时表示值。
- 若查到的是 CONSTANT_Integer_info,取出 4 字节 int 值,以 I 类型压栈(占用 1 个 slot)
- 若查到的是 CONSTANT_Float_info,取出 4 字节 float 值,以 F 类型压栈
- 若查到的是 CONSTANT_String_info,则返回其指向的 java.lang.String 实例引用,以 A 类型压栈
- 若查到的是 CONSTANT_Class_info(Java 7+),则触发该类的初始化(若尚未初始化),然后压入对应的 Class 对象引用
ldc 不做类型转换,只做类型适配
ldc 本身不改变常量值的原始语义,也不会在压栈时进行自动类型提升或收缩。例如:
- 常量池中存的是 int 值 100(CONSTANT_Integer_info),ldc 就压入 int;即使后续指令是 fadd,也需先用 i2f 显式转换,否则校验失败
- 字符串字面量 "123" 存为 CONSTANT_String_info,ldc 压入的是 String 引用,不是整数 123,更不会自动解析成数字
- long 和 double 不支持 ldc(因占 2 个 slot),必须用 ldc2_w,且其常量池项必须是 CONSTANT_Long_info 或 CONSTANT_Double_info
不同 ldc 变体与数据类型对应关系
为覆盖各类常量和更大索引范围,JVM 提供了三个主要变体,各自承担明确职责:
- ldc index:仅支持 8 位索引(0–255),可加载 int/float/String/Class/MethodType/MethodHandle
- ldc_w index1 index2:拼接成 16 位索引(0–65535),功能同 ldc,但突破索引限制,适用于大型类文件
- ldc2_w index1 index2:专用于 long/double,从常量池读取 8 字节值,压入操作数栈时占据连续两个 slot(高位在前)
注意:ldc2_w 不能用于 String 或 int;用 ldc 加载 long 常量会导致 VerifyError。
常见误区与注意事项
实际分析字节码时容易混淆的几点:
- ldc 加载的 String 是运行时常量池中已 intern 的实例,与字符串拼接或 new String() 创建的对象不同,== 比较可能为 true
- ldc 加载 Class 时会触发初始化(clinit 执行),而 Class.forName("X") 默认也会,但 ClassLoader.loadClass() 不会——行为需区分
- 编译器对小整数(-1 到 5)常优先使用 iconst_* 指令(const 系列),而非 ldc,因其更短更快;ldc 多用于非常量范围或字符串等复合类型
- 如果常量池项是未解析的符号引用(如未链接的类名),首次 ldc 会触发解析,可能抛出 NoClassDefFoundError











