JVM字节码加载与存储中的细节

问题引出:

为什么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
    • 类引用等复杂结构

当数值超过 bipushsipushiconst_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 指令设计与编译器优化共同作用的结果

相关推荐
wear工程师10 小时前
Java 类初始化到底何时触发?别把加载、连接和 static 执行混在一起
java·jvm
程序员雷欧11 小时前
线上频繁触发各种 GC 的原因排查与处理指南
java·jvm·算法
wuminyu1 天前
JVM利用io_uring优化堆外内存性能原理剖析
java·linux·c语言·jvm·c++
147API2 天前
Python批量生成蒸馏数据,并发、重试、幂等和断点续跑
开发语言·jvm·python
wuminyu2 天前
JDK21解决虚拟线程IO阻塞原理剖析
java·linux·c语言·jvm·c++
sunywz2 天前
【JVM】(8)JVM调优实战及常量池详解
jvm
wuminyu3 天前
虚拟线程底层ForkJoinPool的工作窃取算法机制
java·linux·c语言·jvm·c++
leisoo80973 天前
股票数据本地化存储实战:JSON、数据库与列式存储的方案对比
jvm·数据库·json
SamChan903 天前
PDF翻译中的并发控制:用Semaphore防止API限流与资源耗尽
java·jvm·pdf
snow@li3 天前
Java:跨平台原理与JDK、JRE、JVM全景深度解析
java·开发语言·jvm