常见字节码指令
加载和存储指令
加载(load)和存储(store)相关的指令是使用得最频繁的指令,分为load类、stroe类、常量加载这三种。
-
load类指令
load类指令是将局部变量表中的变量加载到操作数栈。
比如iload_0将局部变量表中下标为0的int型变量加载到操作数栈上,根据不同的数据变量类型还有lload、fload、dload、aload这些指令,分别代表long、float、double、引用类型的变量。 -
store类指令
store类指令是将栈顶的数据存储到局部变量表中。
比如istore_0是将操作数栈顶的int类型元素存储到局部变量表中下标为0的位置。
根据不同的数据变量类型还有lstore、fstore、dstore、astore这些指令。 -
常量加载相关指令
常见的有const类、push类、ldc类。
const、push类指令是将常量值直接加载到操作数栈顶,如iconst_0是将整数0加载到操作数栈顶,bipush100是将int类型100加载到操作数栈顶。
ldc指令是从常量池加载对应的常量到操作数栈顶,如ldc #10是将常量池中下下标为10的常量数据加载到操作数栈顶。#表示常量池的下标。
常用存储指令
指令 | 描述 |
---|---|
aconst_null | 把null压入栈顶 |
iconst_m1 | 把-1压入栈顶 |
iconst_<n> | 把int类型值(0~5)压入栈顶 |
bipush | 把-128~127的int类型值压入栈顶,如bipush 100 |
ldc | 把常量值从常量池压入栈顶,如ldc #10 |
<T>load | 把类型为T的变量从局部变量表的指定位置压入栈顶,如iload 10。T可为i、l、f、d、a,其中a为引用类型,如aload 0 |
<T>load_<n> | 把类型为T的变量从局部变量表下标为n(0~3)的位置压入栈顶,如iload_1 |
<T>aload | 将指定数组中类型为T的数据从指定位置压入栈顶 |
<T>store | 将栈顶类型为T的数据存储到局部变量表的指定位置,如astore 2 |
<T>store_<n> | 将栈顶类型为T的数据存储到局部变量表下标为n(0~3)的位置 |
<T>astore | 将栈顶类型为T的数据存储到指定数组的指定位置 |
操作数栈指令
常见的操作数栈指令有pop、dup和swap。
指令 | 描述 |
---|---|
pop | 将栈顶的数据出栈(非long和double类型) |
pop2 | 将栈顶的数据出栈(long或double类型)或两个其他类型的数据出栈 |
dup | 复制栈顶数据,并将复制的数据入栈 |
dup_x1 | 复制栈顶数据,并将复制的数据插入栈顶第二个元素之下 |
dup2 | 复制栈顶的两个数据并入栈 |
swap | 交换栈顶的两个元素 |
- pop指令
用于将栈顶的值出栈。一个场景的场景是调用了有返回值的方法,但没有使用这个返回值。
public String foo() {
return "str";
}
public void bar() {
foo(); \\没有接收foo()方法的返回值
}
bar()方法对应的字节码
0: aload_0
1: invokevirtual #13
2: pop \\用来弹出调用bar方法的返回值
3: return
- dup指令
用于复制栈顶的元素并压入栈顶。 - swap指令
用于交换栈顶的两个元素。
运算和类型转换指令
运算符 | 指令(int) | 指令(long) | 指令(float) | 指令(double) | |
---|---|---|---|---|---|
+ | iadd | ladd | fadd | dadd | |
- | isub | lsub | fsub | dsub | |
* | imul | lmul | fmul | dmul | |
/ | idiv | ldiv | fdiv | ddiv | |
% | irem | lrem | frem | drem | |
negate(-:取反) | ineg | lneg | fneg | dneg | |
& | iadn | land | — | — | |
| | ior | lor | — | — | |
^ | ixor | lxor | — | — |
如果两个数据类型不一样的数据做运算,需要进行类型转换。
1.0 + 1的字节码如下:
fconst_1
iconst_1
i2f //将栈顶的1转化为float类型
fadd
有多种类型数据混合运算是,系统会自动将数据转换为范围更大的数据类型,称为宽化类型转换(widening)或自动类型转换。
宽化类型转换并不意味着不会丢失精度。
把范围大的数据类型转换为范围小的数据类型,称为窄化类型转换(narrowing)或强制类型转换。
控制转移指令
指令 | 描述 |
---|---|
ifeq | 如果int类型栈顶元素=0,则跳转 |
ifne | 如果int类型栈顶元素≠0,则跳转 |
iflt | 如果int类型栈顶元素<0,则跳转 |
ifge | 如果int类型栈顶元素>=0,则跳转 |
ifgt | 如果int类型栈顶元素>0,则跳转 |
ifle | 如果int类型栈顶元素<=0,则跳转 |
if_icompeq | 比较栈顶两个int类型元素,=则跳转 |
if_icmpne | 比较栈顶两个int类型元素,≠则跳转 |
if_icmplt | 比较栈顶两个int类型元素,<则跳转 |
if_icmpge | 比较栈顶两个int类型元素,>=则跳转 |
… | … |
goto | 无条件跳转 |
tabelswitch | switch条件跳转,case值紧凑的情况下使用 |
lookupswitch | swithc条件跳转,case值稀疏的情况下使用 |
示例
java代码
public int isPositive(int n) {
if (n > 0) {
return 1;
} else {
return 0;
}
}
字节码
0: iload_1
1: ifle 4
4: iconst_1
5: ireturn
6: iconst_0
7: ireturn
其他
指令 | 描述 |
---|---|
iinc | 直接对局部变量表的数据进行加运行,如iinc 5,1表示对局部变量表下标为5的值+1。该操作无需对局部变量表数据做入栈相加出栈操作 |