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

相关推荐
milanyangbo2 小时前
谁生?谁死?从引用计数到可达性分析,洞悉GC的决策逻辑
java·服务器·开发语言·jvm·后端·算法·架构
m0_748231313 小时前
深入JVM:让Java性能起飞的核心原理与优化策略
java·开发语言·jvm
Vaclee3 小时前
JVM超详解
开发语言·jvm
lqj_本人10 小时前
【Rust编程:从小白入坑】Rust所有权系统
开发语言·jvm·rust
摇滚侠1 天前
Spring Boot3零基础教程,JVM 编译原理,笔记87
jvm·spring boot·笔记
虾..1 天前
C++ 异常
jvm
踩坑小念1 天前
进程 线程 协程基本概念和区别 还有内在联系
java·linux·jvm·操作系统
学习编程的Kitty1 天前
JavaEE初阶——多线程(4)线程安全
java·开发语言·jvm
学到头秃的suhian1 天前
垃圾收集器
java·jvm
Jul1en_1 天前
JVM的内存区域划分、类加载机制与垃圾回收原理
java·jvm