JVM 字节码指令活用手册(基于 Java 17 SE 规范)

JVM 字节码指令活用手册(基于 Java 17 SE 规范)

本手册基于 Java SE 17 JVM 指令集,以**"从代码到字节码"**为核心视角,按照开发者最常见的理解路径重新组织结构,适用于:

  • Java/JVM 学习者:建立从源码到底层的认知桥梁。
  • 字节码工程(ASM/ByteBuddy)开发者:快速定位指令,理解其精确行为。
  • Agent/注解处理器/编译器相关从业者:实现代码生成、分析与修改。
  • 逆向分析、性能调优、面试准备者:洞察 Java 代码的底层执行逻辑。

阅读模型:JVM = 局部变量表 + 操作数栈 + 常量池

所有 JVM 指令都可归入一个简单的数据流体系:

复制代码
局部变量表  ←→  操作数栈  ←→  常量池/对象/方法区
  • 90% 的指令:将"某个位置的值"移动到另一个位置并进行加工。
  • 任何复杂操作:都是对这个三元模型的组合。

指令格式说明

字段 含义
指令 JVM opcode 本体
描述 指令做什么,是否涉及局部变量/栈/对象/跳转
操作数 指令后跟随的字节(若有),如常量池索引、局部变量槽号
栈行为 入栈/出栈顺序,用 ... → ... 表示,... 代表栈顶到栈底
实战场景 该指令在 Java 代码中出现的典型场景
异常 哪些情况会抛异常(如 idiv 除零、数组越界)

1. 常量加载(推常量入栈)

核心思想:将常量池或指令内嵌的值推入操作数栈,是计算的"源头"。

指令 描述 操作数 栈行为 实战场景 异常
aconst_null 推入 null 引用 → null Object o = null;
iconst_m1 推入 int 常量 -1 → -1 int i = -1;
iconst_<i> 推入 int 常量 <i>i ∈ [0,5]) → i int i = 0; / if (i == 1) ...
lconst_<l> 推入 long 常量 <l>l ∈ {0L,1L}) → l long l = 0L;
fconst_<f> 推入 float 常量 <f>f ∈ {0.0,1.0,2.0}) → f float f = 1.0f;
dconst_<d> 推入 double 常量 <d>d ∈ {0.0,1.0}) → d double d = 0.0;
bipush byte 值(-128~127)作为 int 推入栈 1 字节:byte 常量值 → val int i = 100;
sipush short 值(-32768~32767)作为 int 推入栈 2 字节:short 常量值 → val int i = 20000;
ldc 从常量池推入 int/float/String/Class 引用(索引 ≤ 255) 1 字节:常量池索引 → value String s = "hello"; / Class c = String.class;
ldc_w 同上,但支持常量池索引 > 255 2 字节:常量池索引 → value 当常量池很大时
ldc2_w 从常量池推入 long/double(索引 > 255,仅 2 字节索引) 2 字节:常量池索引 → value long l = 123456789L; / double d = 3.14;

设计哲学iconst_<i>, iload_<i> 等指令是为了节省字节码空间 。最常见的 0-3 号局部变量和 -1 到 5 的常量,用单字节指令表示,而不是 iload 0(2字节)。


2. 加载与存储(局部变量表 ↔ 操作数栈)

2.1 局部变量加载(从局部变量表到栈)

指令 描述 操作数 栈行为 实战场景 异常
iload_<i> 加载局部变量 <i>i ∈ [0,3])的 int → val 方法参数、局部变量的读取
lload_<i> 加载局部变量 <i>long → val 同上,long 类型
fload_<i> 加载局部变量 <i>float → val 同上,float 类型
dload_<i> 加载局部变量 <i>double → val 同上,double 类型
aload_<i> 加载局部变量 <i>引用 → ref 对象、数组、this 引用
iload 加载指定索引的 int 局部变量 1 字节:局部变量索引 → val 局部变量索引 > 3 时
lload/fload/dload/aload 同上,对应 long/float/double/引用 1 字节:局部变量索引 → val/ref 同上

2.2 数组元素加载(从数组到栈)

指令 描述 栈行为 实战场景 异常
iaload int[] 加载元素 arrayref, index → val int x = arr[0]; NullPointerException, ArrayIndexOutOfBoundsException
laload/faload/daload long[]/float[]/double[] 加载元素 同上 同上,对应类型 同上
aaload 从引用数组(如 Object[])加载元素 arrayref, index → ref Object o = arr[0]; 同上
baload/caload/saload byte[]/char[]/short[] 加载元素(结果转 int 同上 byte b = barr[0]; (结果为 int 类型) 同上

2.3 局部变量存储(从栈到局部变量表)

指令 描述 操作数 栈行为 实战场景 异常
istore_<i> 栈顶 int 存入局部变量 <i>i ∈ [0,3]) val → int x = 10; (赋值后)
lstore_<i>/fstore_<i>/dstore_<i> 栈顶 long/float/double 存入局部变量 <i> val → 同上,对应类型
astore_<i> 栈顶引用 存入局部变量 <i> ref → String s = "hi"; (赋值后)
istore 栈顶 int 存入指定索引的局部变量 1 字节:局部变量索引 val → 局部变量索引 > 3 时
lstore/fstore/dstore/astore 同上,对应 long/float/double/引用 1 字节:局部变量索引 val/ref → 同上

2.4 数组元素存储(从栈到数组)

指令 描述 栈行为 实战场景 异常
iastore int 值存入 int[] arrayref, index, val → arr[0] = 10; NullPointerException, ArrayIndexOutOfBoundsException
lastore/fastore/dastore long/float/double 值存入对应数组 同上 同上,对应类型 同上
aastore 将引用值存入引用数组 arrayref, index, ref → objArr[0] = new Object(); 同上, ArrayStoreException
bastore/castore/sastore int 值(截断)存入 byte[]/char[]/short[] arrayref, index, val → barr[0] = 10; (val 是 int 类型) 同上

示例:数组操作

java 复制代码
// Java 代码
public void arrayOps() {
    int[] arr = new int[3];
    arr[0] = 100;
    int val = arr[0];
}
bytecode 复制代码
// 对应字节码
0: iconst_3                 // → 3
1: newarray       10 (int)  // 3 → int[3] 的引用
3: astore_1                 // int[3] 引用 → 局部变量 1 (arr)
4: aload_1                  // 局部变量 1 → int[3] 引用
5: iconst_0                 // → 0
6: bipush        100        // → 100
8: iastore                  // int[3] 引用, 0, 100 → (存入数组)
9: aload_1                  // 局部变量 1 → int[3] 引用
10: iconst_0                // → 0
11: iaload                  // int[3] 引用, 0 → 100
12: istore_2                // 100 → 局部变量 2 (val)
13: return

3. 操作数栈管理

核心思想:直接操作栈,用于准备参数、交换值、复制值,是字节码变形的"瑞士军刀"。

指令 描述 栈行为 (栈顶→栈底) 实战场景 异常
pop 弹出栈顶 1 个值 (category 1) a, ... → ... 丢弃一个无用的返回值,如 sb.toString(); 的结果
pop2 弹出栈顶 1 个 category 2 值 或 2 个 category 1 a,b, ... → ...L, ... → ... 丢弃 long/double 返回值或两个 int
dup 复制栈顶值并推入 a, ... → a, a, ... 交换两个局部变量,或用于 new 后的 dup
dup_x1 复制栈顶值,插入到栈顶第二个值之下 a,b, ... → a,b,a, ... 交换栈顶两个值 (swap 的替代实现)
dup_x2 复制栈顶值,插入到栈顶第二/三个值之下 a,b,c, ... → a,b,c,a, ... 复杂交换操作
dup2 复制栈顶 1 个 category 2 或 2 个 category 1 a,b, ... → a,b,a,b, ... 交换两个 long/double
dup2_x1 复制栈顶 2 个槽,插入到下方 a,b,c, ... → a,b,c,a,b, ... 复杂交换操作
dup2_x2 复制栈顶 2 个槽,插入到更下方 a,b,c,d, ... → a,b,c,d,a,b, ... 极其复杂的交换操作,通常由编译器生成
swap 交换栈顶两个 category 1 a,b, ... → b,a, ... 交换两个局部变量的值

示例:new 对象并初始化
new 指令只分配内存,不调用构造函数。构造函数调用需要对象引用。

java 复制代码
// Java 代码
new Object();
bytecode 复制代码
// 对应字节码
0: new           #2 // class java/lang/Object
3: dup                       // 复制对象引用,一份用于 invokespecial,一份作为表达式结果
4: invokespecial #1 // Method java/lang/Object."<init>":()V
7: pop                       // 弹出构造函数返回值

栈变化图解

  1. new: → ref
  2. dup: ref → ref, ref
  3. invokespecial: ref, ref → ref (第一个 ref 被消耗,第二个 ref 留在栈顶)
  4. pop: ref →

4. 数学运算与类型转换

4.1 数学运算(栈顶值计算)

指令 描述 栈行为 实战场景 可能异常
加法 iadd/ladd/fadd/dadd a,b → a+b c = a + b; 无(浮点溢出返回特殊值)
减法 isub/lsub/fsub/dsub a,b → a-b c = a - b;
乘法 imul/lmul/fmul/dmul a,b → a*b c = a * b;
除法 idiv/ldiv/fdiv/ddiv a,b → a/b c = a / b; idiv/ldiv 除数为 0 抛出 ArithmeticException
取余 irem/lrem/frem/drem a,b → a%b c = a % b; 同上
取反 ineg/lneg/fneg/dneg a → -a c = -a;
增量 iinc 直接修改局部变量 for (int i=0;...; i++) 中的 i++

4.2 类型转换(栈顶值转换)

指令 描述 栈行为 转换规则 实战场景
宽化转换(无精度损失)
i2l intlong i → l 符号扩展 long l = (long)i;
i2f intfloat i → f 可能丢失精度(但范围扩大) float f = (float)i;
i2d intdouble i → d 无损失 double d = (double)i;
l2f longfloat l → f 可能丢失精度 float f = (float)l;
l2d longdouble l → d 无损失 double d = (double)l;
f2d floatdouble f → d 无损失 double d = (double)f;
窄化转换(可能损失精度/符号)
l2i longint l → i 截断高位 int i = (int)l;
f2i floatint f → i 截断小数,溢出返回 Integer.MIN_VALUE/MAX_VALUE int i = (int)f;
d2i doubleint d → i 同上 int i = (int)d;
窄化整数转换(截断)
i2b intbyte(结果转 int i → i 截断为 8 位 byte b = (byte)i;
i2c intchar(结果转 int i → i 截断为 16 位,无符号 char c = (char)i;
i2s intshort(结果转 int i → i 截断为 16 位 short s = (short)i;

5. 对象创建与操作

指令 描述 操作数 栈行为 实战场景 可能异常
new 创建对象实例(不调用构造函数 2 字节:常量池索引(类符号引用) → ref new MyClass(); OutOfMemoryError
newarray 创建基本类型数组 1 字节:元素类型编码(如 T_INT=10) size → arrayref new int[10]; NegativeArraySizeException
anewarray 创建引用类型数组 2 字节:常量池索引(元素类型符号引用) size → arrayref new String[10]; 同上
multianewarray 创建多维数组 2 字节:常量池索引;1 字节:维度 size1, size2... → arrayref new int[2][3]; 同上
arraylength 获取数组长度(结果为 int arrayref → len int len = arr.length; NullPointerException
athrow 抛出栈顶异常对象 ref → (异常抛出) throw new Exception();
checkcast 检查类型转换合法性 2 字节:常量池索引(目标类型符号引用) ref → ref (String) obj; ClassCastException
instanceof 检查对象是否为指定类型实例(结果 int 0/1) 2 字节:常量池索引(类型符号引用) ref → 0/1 if (obj instanceof String) {...}
getclass 获取对象的运行时类(Class 引用) ref → classref Class c = obj.getClass(); NullPointerException
monitorenter 进入对象监视器(synchronized 块开始) ref → synchronized(obj) { ... } NullPointerException
monitorexit 退出对象监视器(synchronized 块结束) ref → synchronized 块结束处 同上

6. 字段与方法调用

6.1 字段访问

指令 描述 操作数 栈行为 实战场景 可能异常
getstatic 获取静态字段 2 字节:常量池索引 → value int x = MyClass.staticField; 无(链接时验证)
putstatic 存入静态字段 同上 value → MyClass.staticField = x;
getfield 获取实例字段 同上 ref → value int x = obj.instanceField; NullPointerException
putfield 存入实例字段 同上 ref, value → obj.instanceField = x; 同上

6.2 方法调用

指令 描述 操作数 栈行为 实战场景 调用机制
invokevirtual 调用实例方法(动态分派) 2 字节:常量池索引 this, args... → result obj.normalMethod(); 基于对象实际类型查找方法(虚调用)
invokespecial 调用实例方法(静态分派) 同上 this, args... → result super.method(); / 构造函数调用 / private 方法调用 不动态分派,直接绑定符号引用中的方法
invokestatic 调用静态方法 同上 args... → result MyClass.staticMethod(); 静态绑定,无 this
invokeinterface 调用接口方法(动态分派) 同上;1 字节:参数数量;1 字节:0 this, args... → result myInterfaceMethod(); 基于对象实际类型查找实现类方法,比 invokevirtual 稍慢
invokedynamic 动态调用(Lambda/动态语言基础) 2 字节:常量池索引;2 字节:0 args... → result () -> System.out.println("Lambda"); 首次执行时通过引导方法链接目标方法

示例:多态调用 (invokevirtual)

java 复制代码
// Java 代码
class Parent { void who() { System.out.println("Parent"); } }
class Child extends Parent { @Override void who() { System.out.println("Child"); } }
public void test() {
    Parent p = new Child();
    p.who(); // 动态分派
}
bytecode 复制代码
// 对应字节码
0: new           #2 // class Child
3: dup
4: invokespecial #3 // Method Child."<init>":()V
7: astore_1
8: aload_1                  // 加载 Parent 类型的引用 p
9: invokevirtual #4 // Method Parent.who:()V  // 关键:调用 Parent.who,但实际执行 Child.who
12: return

7. 控制流与返回

7.1 条件跳转

指令类型 指令 描述 栈行为 实战场景
int 与 0 比较 ifeq/ifne 栈顶 int 等于/不等于 0 则跳转 i → if (i == 0) ... / if (i != 0) ...
iflt/ifle/ifgt/ifge 栈顶 int < / ≤ / > / ≥ 0 则跳转 i → if (i < 0) ...
int 间比较 if_icmpeq/if_icmpne 栈顶两个 int 相等/不相等则跳转 i1, i2 → if (a == b) ...
if_icmplt/if_icmple/if_icmpgt/if_icmpge 栈顶两个 int < / ≤ / > / ≥ 则跳转 i1, i2 → if (a < b) ...
引用比较 if_acmpeq/if_acmpne 栈顶两个引用相等/不相等则跳转 ref1, ref2 → if (a == b) ... (对象引用)
null 判断 ifnull/ifnonnull 栈顶引用为 null/非 null 则跳转 ref → if (obj != null) ...

7.2 无条件跳转与 switch

指令 描述 操作数 实战场景
goto 无条件跳转 2 字节:相对偏移量 for/while 循环、if-elseelse 分支
goto_w 宽索引版无条件跳转 4 字节:相对偏移量 方法过长,goto 偏移量不够时
tableswitch 连续 case 值的高效 switch 默认偏移量、case 最小值、case 最大值、case 偏移量表 switch (day) { case 1: ... case 2: ... }
lookupswitch 稀疏 case 值的 switch 默认偏移量、case 数量、case-value-偏移量对列表 switch (type) { case 1: ... case 100: ... }

示例:if-else

java 复制代码
// Java 代码
if (a > b) {
    c = 1;
} else {
    c = 2;
}
bytecode 复制代码
// 对应字节码
iload_0                  // → a
iload_1                  // → b
if_icmple 11             // a, b → (如果 a <= b, 跳转到偏移量 11)
iconst_1                 // → 1
istore_2                 // 1 → c
goto 13                  // 跳转到结尾
11: iconst_2             // → 2
istore_2                 // 2 → c
13: ...

7.3 返回指令

指令 描述 栈行为 适用方法类型
ireturn 返回 int i → (返回给调用者) int 返回值方法
lreturn 返回 long l → long 返回值方法
freturn 返回 float f → float 返回值方法
dreturn 返回 double d → double 返回值方法
areturn 返回引用 ref → 引用类型返回值方法
return 返回 void void 方法

8. 扩展与调试指令

指令 描述 操作数 实战场景
wide 扩展后续指令的局部变量索引为 16 位 1 字节:被扩展的指令;2 字节:局部变量索引;若为 iinc 再加 2 字节增量 支持局部变量索引 > 255
nop 无操作 调试器占位、代码对齐
breakpoint 调试器断点指令 调试器使用

总结:指令助记符速记口诀

  • 常量加载 :小常量直接推(iconst_<i>),大常量用 ldcnullaconst_null
  • 加载存储 :类型前缀 (i/l/f/d/a) + load/store_<i> 是 0-3 速记,数组操作带 aiaload)。
  • 栈管理pop 弹,dup 复制,swap 交换,nop 占位,dup_x* 是高级技巧。
  • 数学运算 :类型前缀 + 操作(iadd),iinc 直接改局部变量。
  • 对象操作new 创对象,newarray 创数组,checkcast 检类型,instanceof 做判断。
  • 方法调用virtual 虚调用,special 静态派,static 调静态,interface 调接口,dynamic 动态联。
  • 控制流if 开头条件跳,goto 无条件,tableswitch 连续 case,lookupswitch 稀疏 case。
  • 返回指令 :类型前缀 + returnvoid 直接 return

相关推荐
2501_906150563 小时前
私有部署问卷系统操作实战记录-DWSurvey
java·运维·服务器·spring·开源
better_liang3 小时前
每日Java面试场景题知识点之-TCP/IP协议栈与Socket编程
java·tcp/ip·计算机网络·网络编程·socket·面试题
niucloud-admin3 小时前
java服务端——controller控制器
java·开发语言
To Be Clean Coder3 小时前
【Spring源码】通过 Bean 工厂获取 Bean 的过程
java·后端·spring
Fortunate Chen4 小时前
类与对象(下)
java·javascript·jvm
程序员水自流4 小时前
【AI大模型第9集】Function Calling,让AI大模型连接外部世界
java·人工智能·llm
‿hhh4 小时前
综合交通运行协调与应急指挥平台项目说明
java·ajax·npm·json·需求分析·个人开发·规格说明书
小徐Chao努力4 小时前
【Langchain4j-Java AI开发】06-工具与函数调用
java·人工智能·python
无心水4 小时前
【神经风格迁移:全链路压测】33、全链路监控与性能优化最佳实践:Java+Python+AI系统稳定性保障的终极武器
java·python·性能优化
萧曵 丶4 小时前
Synchronized 详解及 JDK 版本优化
java·多线程·synchronized