问题引出:
为什么Java定义int型变量为32767时使用的是bipush 32767,而定义int型变量为32768时使用的是 ldc #4 <32768> ?
在 Java 中,如果这样定义int型变量:
            
            
              java
              
              
            
          
          public class Test {
    public static void main(String[] args) {
        int i = 0;
        int j = 5;
        int k = 6;
        int m = 32768;
        int n = 32767;
    }
}变量对应的字节码文件内容是这样的:
            
            
              java
              
              
            
          
           0 iconst_0
 1 istore_1
 2 iconst_5
 3 istore_2
 4 bipush 6
 6 istore_3
 7 ldc #2 <32768>
 9 istore 4
11 sipush 32767
14 istore 5
16 return其中可以看到,当变量值为0~5时,使用的是iconst_x(x为值)的命令来操作的,当变量值为6时,采用bipush 6的命令来操作,但当值为32768时,却采用了ldc命令结合常量池的形式,但是在值为32767时,仍然使用的是sipush 32767的形式来操作的,于是好奇为什么相邻的int值采用了不同的命令来操作。
猜想:与Integer在-128到127之间的值是直接从缓存中取的思路可能一致,为了保证效率而做的优化。
查找资料后,得到的答案是:
核心原因:
是由于 JVM 指令集的设计限制和效率考虑导致的:
✅ bipush 的作用与限制:
- bipush(byte integer push):将一个 单字节有符号整数 (即范围为- -128 ~ 127)推入操作数栈。
- 它后面跟的是一个 8位带符号整数(即只能表示 -128 到 127)。
但是!
Java 编译器做了一个"扩展",它允许你在某些情况下使用 bipush 来处理更大一点的整数,比如 short 类型范围内的(-32768 到 32767),但本质上仍然是把值当作 byte 扩展成 int 处理。
不过,一旦超过 127(或小于 -128),就不能再使用 bipush,因为它的参数是 1 字节的有符号整数。
所以:
- 32767超过了- bipush支持的范围(-128 ~ 127),但它仍然能被优化地编码进字节码;
- 32768就不能使用- bipush,必须使用更通用的方式加载,如- ldc。
✅ ldc 是什么?
- ldc是 "load constant" 的缩写。
- 它从 运行时常量池(Runtime Constant Pool)中加载一个常量值到操作数栈。
- 可以用于加载:
- int
- float
- String
- 类引用等复杂结构
 
当数值超过 bipush、sipush 或 iconst_x 等直接加载指令支持的范围时,Java 编译器就会将其放入常量池,并使用 ldc 加载。
具体分析:
| 数值 | 使用指令 | 原因说明 | 
|---|---|---|
| -1 | iconst_m1 | 特殊指令 | 
| 0 ~ 5 | iconst_0,iconst_1...iconst_5 | 有专门的短指令 | 
| 6 ~ 127 | bipush x | 单字节可表示 | 
| 128 ~ 32767 | sipush x | 使用双字节有符号整数 | 
| ≥ 32768 | ldc #x | 放入常量池,使用 ldc加载 | 
所以:
- 32767可以用- sipush 32767(因为是 short 最大值)
- 32768已经超过了- short的最大值(32767),只能用- ldc加载
✅ 总结:
| 指令 | 支持范围 | 用途说明 | 
|---|---|---|
| iconst_x | -1 ~ 5 | 特定小整数专用指令 | 
| bipush | -128 ~ 127 | 单字节整数 | 
| sipush | -32768 ~ 32767 | 双字节整数 | 
| ldc | 任意整数(放入常量池即可) | 加载超出上述范围的整数 | 
❗ 结论:
- 32767属于- sipush支持的最大值范围内,所以用- sipush 32767
- 32768超出- sipush范围,必须使用- ldc从常量池加载
这就是会看到不同指令的原因 ------ 是 JVM 指令设计与编译器优化共同作用的结果。