Java 编程/字节码
外观
< Java 编程
Java 字节码是 Java 源代码被编译成的语言,也是 Java 虚拟机可以理解的语言。与必须为每种不同类型的计算机专门编译的编译语言不同,Java 程序只需要转换一次为字节码,之后它可以在任何存在 Java 虚拟机的平台上运行。
字节码是 Java 程序的编译格式。一旦 Java 程序被转换为字节码,它就可以通过网络传输并由 Java 虚拟机 (JVM) 执行。字节码文件通常具有 .class 扩展名。对于 Java 程序员来说,通常不需要了解字节码,但它可能很有用。
有一些激动人心的新语言正在被创建,它们也编译成 Java 字节码,例如 Groovy。
- GNAT
- GNU Ada 编译器,能够将 Ada 编译成 Java 风格的字节码。
- ftp://cs.nyu.edu/pub/gnat
- JPython
- 将 Python 编译成 Java 风格的字节码。
- http://www.jpython.org/
- Kawa
- 将 Scheme 编译成 Java 风格的字节码。
- http://www.gnu.org/software/kawa/
考虑以下 Java 代码。
outer:
for (int i = 2; i < 1000; i++) {
for (int j = 2; j < i; j++) {
if (i % j == 0)
continue outer;
}
System.out.println (i);
}
Java 编译器可能会将上面的 Java 代码翻译成以下字节码,假设上面的代码被放在一个方法中
Code:
0: iconst_2
1: istore_1
2: iload_1
3: sipush 1000
6: if_icmpge 44
9: iconst_2
10: istore_2
11: iload_2
12: iload_1
13: if_icmpge 31
16: iload_1
17: iload_2
18: irem # remainder
19: ifne 25
22: goto 38
25: iinc 2, 1
28: goto 11
31: getstatic #84; //Field java/lang/System.out:Ljava/io/PrintStream;
34: iload_1
35: invokevirtual #85; //Method java/io/PrintStream.println:(I)V
38: iinc 1, 1
41: goto 2
44: return
例如,我们可以编写一个简单的 Foo.java 源文件
public class Foo {
public static void main(final String[] args) {
System.out.println("This is a simple example of decompilation using javap");
a();
b();
}
public static void a() {
System.out.println("Now we are calling a function...");
}
public static void b() {
System.out.println("...and now we are calling b");
}
}
编译它,然后将 Foo.java 移动到另一个目录,或者如果你愿意,删除它。我们可以用 javap 和 Foo.class 做什么?
$javap Foo
产生以下结果
Compiled from "Foo.java" public class Foo extends java.lang.Object { public Foo(); public static void main(java.lang.String[]); public static void a(); public static void b(); }
如你所见,javac 编译器不会从 .class 文件中剥离任何(公共)变量名。因此,函数的名称、它们的參数和返回值的类型都暴露出来了。(这是为了让其他类能够访问它们所必需的。)
让我们再做一些,试试
$javap -c Foo
Compiled from "Foo.java" public class Foo extends java.lang.Object{ public Foo(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3; //String This is a simple example of decompilation using javap 5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: invokestatic #5; //Method a:()V 11: invokestatic #6; //Method b:()V 14: return public static void a(); Code: 0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #7; //String Now we are calling a function... 5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return public static void b(); Code: 0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #8; //String ...and now we are calling b 5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return }
有关更详细的描述,请参阅 Oracle 的 Java 虚拟机规范[1]
操作数栈的操作表示为 [before]→[after],其中 [before] 是执行指令之前栈的状态,[after] 是执行指令之后栈的状态。一个栈,其顶部元素为 'b',其下一层元素为 'a',表示为 'a,b'。
助记符 | 操作码 (以 十六进制 表示) |
其他字节 | 栈 [before]→[after] |
描述 |
---|---|---|---|---|
A | ||||
aaload | 32 | arrayref, index → value | 从数组中加载引用到堆栈 | |
aastore | 53 | arrayref, index, value → | 将引用存储到数组中 | |
aconst_null | 01 | → null | 将一个 null 引用推送到堆栈 | |
aload | 19 | index | → objectref | 从本地变量 #index 中加载引用到堆栈 |
aload_0 | 2a | → objectref | 从本地变量 0 中加载引用到堆栈 | |
aload_1 | 2b | → objectref | 从本地变量 1 中加载引用到堆栈 | |
aload_2 | 2c | → objectref | 从本地变量 2 中加载引用到堆栈 | |
aload_3 | 2d | → objectref | 从本地变量 3 中加载引用到堆栈 | |
anewarray | bd | indexbyte1, indexbyte2 | count → arrayref | 创建一个新的引用数组,长度为 count,组件类型由常量池中 index (indexbyte1 << 8 + indexbyte2) 标识的类引用指定 |
areturn | b0 | objectref → [empty] | 从方法中返回一个引用 | |
arraylength | be | arrayref → length | 获取数组的长度 | |
astore | 3a | index | objectref → | 将引用存储到本地变量 #index 中 |
astore_0 | 4b | objectref → | 将引用存储到本地变量 0 中 | |
astore_1 | 4c | objectref → | 将引用存储到本地变量 1 中 | |
astore_2 | 4d | objectref → | 将引用存储到本地变量 2 中 | |
astore_3 | 4e | objectref → | 将引用存储到本地变量 3 中 | |
athrow | bf | objectref → [empty], objectref | 抛出错误或异常(注意,其余堆栈会被清空,只留下对 Throwable 的引用) | |
B | ||||
baload | 33 | arrayref, index → value | 从数组中加载一个字节或布尔值 | |
bastore | 54 | arrayref, index, value → | 将一个字节或布尔值存储到数组中 | |
bipush | 10 | byte | → value | 将一个 byte 推送到堆栈作为整数 value |
C | ||||
caload | 34 | arrayref, index → value | 从数组中加载一个字符 | |
castore | 55 | arrayref, index, value → | 将一个字符存储到数组中 | |
checkcast | c0 | indexbyte1, indexbyte2 | objectref → objectref | 检查一个 objectref 是否是某种类型,该类型的类引用位于常量池中的 index (indexbyte1 << 8 + indexbyte2) 位置 |
D | ||||
d2f | 90 | value → result | 将一个双精度浮点数转换为单精度浮点数 | |
d2i | 8e | value → result | 将一个双精度浮点数转换为整数 | |
d2l | 8f | value → result | 将一个双精度浮点数转换为长整型 | |
dadd | 63 | value1, value2 → result | 将两个双精度浮点数相加 | |
daload | 31 | arrayref, index → value | 从数组中加载一个双精度浮点数 | |
dastore | 52 | arrayref, index, value → | 将一个双精度浮点数存储到数组中 | |
dcmpg | 98 | value1, value2 → result | 比较两个双精度浮点数 | |
dcmpl | 97 | value1, value2 → result | 比较两个双精度浮点数 | |
dconst_0 | 0e | → 0.0 | 将常量 0.0 推送到堆栈 | |
dconst_1 | 0f | → 1.0 | 将常量 1.0 推送到堆栈 | |
ddiv | 6f | value1, value2 → result | 将两个双精度浮点数相除 | |
dload | 18 | index | → value | 从本地变量 #index 中加载一个双精度浮点数 value |
dload_0 | 26 | → value | 从本地变量 0 中加载一个双精度浮点数 | |
dload_1 | 27 | → value | 从本地变量 1 中加载一个双精度浮点数 | |
dload_2 | 28 | → value | 从本地变量 2 中加载一个双精度浮点数 | |
dload_3 | 29 | → value | 从本地变量 3 中加载一个双精度浮点数 | |
dmul | 6b | value1, value2 → result | 将两个双精度浮点数相乘 | |
dneg | 77 | value → result | 对一个双精度浮点数取负 | |
drem | 73 | value1, value2 → result | 获取两个双精度浮点数相除的余数 | |
dreturn | af | value → [empty] | 从方法中返回一个双精度浮点数 | |
dstore | 39 | index | value → | 将一个双精度浮点数 value 存储到本地变量 #index 中 |
dstore_0 | 47 | value → | 将一个双精度浮点数存储到本地变量 0 中 | |
dstore_1 | 48 | value → | 将一个双精度浮点数存储到本地变量 1 中 | |
dstore_2 | 49 | value → | 将一个双精度浮点数存储到本地变量 2 中 | |
dstore_3 | 4a | value → | 将一个双精度浮点数存储到本地变量 3 中 | |
dsub | 67 | value1, value2 → result | 从另一个双精度浮点数中减去一个双精度浮点数 | |
dup | 59 | value → value, value | 复制堆栈顶部的值 | |
dup_x1 | 5a | value2, value1 → value1, value2, value1 | 将顶部值的副本插入到距离顶部两个值的堆栈中 | |
dup_x2 | 5b | value3, value2, value1 → value1, value3, value2, value1 | 将顶部值的副本插入到距离顶部两个(如果 value2 是双精度浮点数或长整型,它也会占用 value3 的位置)或三个(如果 value2 既不是双精度浮点数也不是长整型)值的堆栈中 | |
dup2 | 5c | {value2, value1} → {value2, value1}, {value2, value1} | 复制堆栈顶部的两个字(两个值,如果 value1 既不是双精度浮点数也不是长整型;一个值,如果 value1 是双精度浮点数或长整型) | |
dup2_x1 | 5d | value3, {value2, value1} → {value2, value1}, value3, {value2, value1} | 复制两个字并将它们插入到第三个字的下方(见上文解释) | |
dup2_x2 | 5e | {value4, value3}, {value2, value1} → {value2, value1}, {value4, value3}, {value2, value1} | 复制两个字并将它们插入到第四个字的下方 | |
F | ||||
f2d | 8d | value → result | 将一个单精度浮点数转换为双精度浮点数 | |
f2i | 8b | value → result | 将一个单精度浮点数转换为整数 | |
f2l | 8c | value → result | 将一个单精度浮点数转换为长整型 | |
fadd | 62 | value1, value2 → result | 将两个单精度浮点数相加 | |
faload | 30 | arrayref, index → value | 从数组中加载一个单精度浮点数 | |
fastore | 51 | arreyref, index, value → | 将一个单精度浮点数存储到数组中 | |
fcmpg | 96 | value1, value2 → result | 比较两个浮点数 | |
fcmpl | 95 | value1, value2 → result | 比较两个浮点数 | |
fconst_0 | 0b | → 0.0f | 将0.0f压入堆栈 | |
fconst_1 | 0c | → 1.0f | 将1.0f压入堆栈 | |
fconst_2 | 0d | → 2.0f | 将2.0f压入堆栈 | |
fdiv | 6e | value1, value2 → result | 除两个浮点数 | |
fload | 17 | index | → value | 从局部变量#index加载浮点数value |
fload_0 | 22 | → value | 从局部变量0加载浮点数value | |
fload_1 | 23 | → value | 从局部变量1加载浮点数value | |
fload_2 | 24 | → value | 从局部变量2加载浮点数value | |
fload_3 | 25 | → value | 从局部变量3加载浮点数value | |
fmul | 6a | value1, value2 → result | 乘两个浮点数 | |
fneg | 76 | value → result | 取浮点数的负值 | |
frem | 72 | value1, value2 → result | 获取两个浮点数相除的余数 | |
freturn | ae | value → [empty] | 从方法返回一个浮点数 | |
fstore | 38 | index | value → | 将浮点数value存储到局部变量#index |
fstore_0 | 43 | value → | 将浮点数value存储到局部变量0 | |
fstore_1 | 44 | value → | 将浮点数value存储到局部变量1 | |
fstore_2 | 45 | value → | 将浮点数value存储到局部变量2 | |
fstore_3 | 46 | value → | 将浮点数value存储到局部变量3 | |
fsub | 66 | value1, value2 → result | 减去两个浮点数 | |
G | ||||
getfield | b4 | index1, index2 | objectref → value | 获取对象objectref的字段value,其中字段由常量池index中的字段引用标识(index1 << 8 + index2) |
getstatic | b2 | index1, index2 | → value | 获取类的静态字段value,其中字段由常量池index中的字段引用标识(index1 << 8 + index2) |
goto | a7 | branchbyte1, branchbyte2 | [无变化] | 转到branchoffset处的另一个指令(从无符号字节branchbyte1 << 8 + branchbyte2构造的带符号短整型) |
goto_w | c8 | branchbyte1, branchbyte2, branchbyte3, branchbyte4 | [无变化] | 转到branchoffset处的另一个指令(从无符号字节branchbyte1 << 24 + branchbyte2 << 16 + branchbyte3 << 8 + branchbyte4构造的带符号整型) |
I | ||||
i2b | 91 | value → result | 将int转换为字节 | |
i2c | 92 | value → result | 将int转换为字符 | |
i2d | 87 | value → result | 将int转换为双精度浮点数 | |
i2f | 86 | value → result | 将int转换为单精度浮点数 | |
i2l | 85 | value → result | 将int转换为长整型 | |
i2s | 93 | value → result | 将int转换为短整型 | |
iadd | 60 | value1, value2 → result | 将两个int加在一起 | |
iaload | 2e | arrayref, index → value | 从数组中加载一个int | |
iand | 7e | value1, value2 → result | 对两个整数执行逻辑与运算 | |
iastore | 4f | arrayref, index, value → | 将一个int存储到数组中 | |
iconst_m1 | 02 | → -1 | 将int值-1加载到堆栈中 | |
iconst_0 | 03 | → 0 | 将int值0加载到堆栈中 | |
iconst_1 | 04 | → 1 | 将int值1加载到堆栈中 | |
iconst_2 | 05 | → 2 | 将int值2加载到堆栈中 | |
iconst_3 | 06 | → 3 | 将int值3加载到堆栈中 | |
iconst_4 | 07 | → 4 | 将int值4加载到堆栈中 | |
iconst_5 | 08 | → 5 | 将int值5加载到堆栈中 | |
idiv | 6c | value1, value2 → result | 除两个整数 | |
if_acmpeq | a5 | branchbyte1, branchbyte2 | value1, value2 → | 如果引用相等,则跳转到branchoffset处的指令(从无符号字节branchbyte1 << 8 + branchbyte2构造的带符号短整型) |
if_acmpne | a6 | branchbyte1, branchbyte2 | value1, value2 → | 如果引用不相等,则跳转到branchoffset处的指令(从无符号字节branchbyte1 << 8 + branchbyte2构造的带符号短整型) |
if_icmpeq | 9f | branchbyte1, branchbyte2 | value1, value2 → | 如果int相等,则跳转到branchoffset处的指令(从无符号字节branchbyte1 << 8 + branchbyte2构造的带符号短整型) |
if_icmpne | a0 | branchbyte1, branchbyte2 | value1, value2 → | 如果int不相等,则跳转到branchoffset处的指令(从无符号字节branchbyte1 << 8 + branchbyte2构造的带符号短整型) |
if_icmplt | a1 | branchbyte1, branchbyte2 | value1, value2 → | 如果value1小于value2,则跳转到branchoffset处的指令(从无符号字节branchbyte1 << 8 + branchbyte2构造的带符号短整型) |
if_icmpge | a2 | branchbyte1, branchbyte2 | value1, value2 → | 如果value1大于或等于value2,则跳转到branchoffset处的指令(从无符号字节branchbyte1 << 8 + branchbyte2构造的带符号短整型) |
if_icmpgt | a3 | branchbyte1, branchbyte2 | value1, value2 → | 如果value1大于value2,则跳转到branchoffset处的指令(从无符号字节branchbyte1 << 8 + branchbyte2构造的带符号短整型) |
if_icmple | a4 | branchbyte1, branchbyte2 | value1, value2 → | 如果value1小于或等于value2,则跳转到branchoffset处的指令(从无符号字节branchbyte1 << 8 + branchbyte2构造的带符号短整型) |
ifeq | 99 | branchbyte1, branchbyte2 | value → | 如果value为0,则跳转到branchoffset处的指令(从无符号字节branchbyte1 << 8 + branchbyte2构造的带符号短整型) |
ifne | 9a | branchbyte1, branchbyte2 | value → | 如果value不为0,则跳转到branchoffset处的指令(从无符号字节branchbyte1 << 8 + branchbyte2构造的带符号短整型) |
iflt | 9b | branchbyte1, branchbyte2 | value → | 如果value小于0,则跳转到branchoffset处的指令(从无符号字节branchbyte1 << 8 + branchbyte2构造的带符号短整型) |
ifge | 9c | branchbyte1, branchbyte2 | value → | 如果value大于或等于0,则跳转到branchoffset处的指令(从无符号字节branchbyte1 << 8 + branchbyte2构造的带符号短整型) |
ifgt | 9d | branchbyte1, branchbyte2 | value → | 如果value大于0,则跳转到branchoffset处的指令(从无符号字节branchbyte1 << 8 + branchbyte2构造的带符号短整型) |
ifle | 9e | branchbyte1, branchbyte2 | value → | 如果value小于或等于0,则跳转到branchoffset处的指令(从无符号字节branchbyte1 << 8 + branchbyte2构造的带符号短整型) |
ifnonnull | c7 | branchbyte1, branchbyte2 | value → | 如果value不为空,则跳转到branchoffset处的指令(从无符号字节branchbyte1 << 8 + branchbyte2构造的带符号短整型) |
ifnull | c6 | branchbyte1, branchbyte2 | value → | 如果value为空,则跳转到branchoffset处的指令(从无符号字节branchbyte1 << 8 + branchbyte2构造的带符号短整型) |
iinc | 84 | index, const | [无变化] | 将局部变量#index增加带符号字节const |
iload | 15 | index | → value | 从变量#index加载一个intvalue |
iload_0 | 1a | → value | 从变量0加载一个intvalue | |
iload_1 | 1b | → value | 从变量1加载一个intvalue | |
iload_2 | 1c | → value | 从变量2加载一个intvalue | |
iload_3 | 1d | → value | 从变量3加载一个intvalue | |
imul | 68 | value1, value2 → result | 乘两个整数 | |
ineg | 74 | value → result | 取int的负值 | |
instanceof | c1 | indexbyte1, indexbyte2 | objectref → result | 确定对象objectref是否为给定类型,由常量池(indexbyte1 << 8 + indexbyte2)中的类引用index标识 |
invokedynamic | ba | indexbyte1, indexbyte2 | [arg1, arg2, ...] → result | 调用动态方法并将结果放到堆栈上(可能是void);该方法由常量池(indexbyte1 << 8 | indexbyte2)中的方法引用index标识 |
invokeinterface | b9 | indexbyte1, indexbyte2, count, 0 | objectref, [arg1, arg2, ...] → | 在对象objectref上调用接口方法,其中接口方法由常量池(indexbyte1 << 8 + indexbyte2)中的方法引用index标识,count是从堆栈帧中弹出的参数数量,包括调用方法的对象,并且必须始终大于或等于1 |
invokespecial | b7 | indexbyte1, indexbyte2 | objectref, [arg1, arg2, ...] → | 调用对象objectref上的实例方法,需要特殊处理(实例初始化方法、私有方法或超类方法),其中方法由常量池(indexbyte1 << 8 + indexbyte2)中的方法引用index标识 |
invokestatic | b8 | indexbyte1, indexbyte2 | [arg1, arg2, ...] → | 调用静态方法,其中方法由常量池(indexbyte1 << 8 + indexbyte2)中的方法引用index标识 |
invokevirtual | b6 | indexbyte1, indexbyte2 | objectref, [arg1, arg2, ...] → | 调用对象objectref上的虚拟方法,其中方法由常量池(indexbyte1 << 8 + indexbyte2)中的方法引用index标识 |
ior | 80 | value1, value2 → result | 逻辑int或 | |
irem | 70 | value1, value2 → result | 逻辑int余数 | |
ireturn | ac | value → [empty] | 从方法返回一个整数 | |
ishl | 78 | value1, value2 → result | int左移 | |
ishr | 7a | value1, value2 → result | int右移 | |
istore | 36 | index | value → | 将intvalue存储到变量#index中 |
istore_0 | 3b | value → | 将intvalue存储到变量0中 | |
istore_1 | 3c | value → | 将intvalue存储到变量1中 | |
istore_2 | 3d | value → | 将intvalue存储到变量2中 | |
istore_3 | 3e | value → | 将intvalue存储到变量3中 | |
isub | 64 | value1, value2 → result | int减法 | |
iushr | 7c | value1, value2 → result | int右移 | |
ixor | 82 | value1, value2 → result | int异或 | |
J | ||||
jsr | a8 | branchbyte1, branchbyte2 | → address | 跳转到branchoffset处的子程序并把返回地址放到堆栈上(从无符号字节branchbyte1 << 8 + branchbyte2构造的带符号短整型) |
jsr_w | c9 | branchbyte1, branchbyte2, branchbyte3, branchbyte4 | → address | 跳转到branchoffset处的子程序并把返回地址放到堆栈上(从无符号字节branchbyte1 << 24 + branchbyte2 << 16 + branchbyte3 << 8 + branchbyte4构造的带符号整型) |
L | ||||
l2d | 8a | value → result | 将长整型转换为双精度浮点数 | |
l2f | 89 | value → result | 将长整型转换为单精度浮点数 | |
l2i | 88 | value → result | 将长整型转换为整数 | |
ladd | 61 | value1, value2 → result | 将两个长整型加在一起 | |
laload | 2f | arrayref, index → value | 从数组中加载一个长整型 | |
land | 7f | value1, value2 → result | 两个长整型的按位与运算 | |
lastore | 50 | arrayref, index, value → | 将一个长整型存储到数组中 | |
lcmp | 94 | value1, value2 → result | 比较两个长整型值 | |
lconst_0 | 09 | → 0L | 将长整型0压入堆栈 | |
lconst_1 | 0a | → 1L | 将长整型1压入堆栈 | |
ldc | 12 | index | → value | 将常量池(字符串、int、float 或类类型)中的常量#index压入堆栈 |
ldc_w | 13 | indexbyte1, indexbyte2 | → value | 将常量池(字符串、int、float 或类类型)中的常量#index压入堆栈(宽index构造为indexbyte1 << 8 + indexbyte2) |
ldc2_w | 14 | indexbyte1, indexbyte2 | → value | 将常量池(双精度浮点数或长整型)中的常量#index压入堆栈(宽index构造为indexbyte1 << 8 + indexbyte2) |
ldiv | 6d | value1, value2 → result | 将两个长整型数相除 | |
lload | 16 | index | → value | 从局部变量 #index 中加载一个长整型值 |
lload_0 | 1e | → value | 从局部变量 0 中加载一个长整型值 | |
lload_1 | 1f | → value | 从局部变量 1 中加载一个长整型值 | |
lload_2 | 20 | → value | 从局部变量 2 中加载一个长整型值 | |
lload_3 | 21 | → value | 从局部变量 3 中加载一个长整型值 | |
lmul | 69 | value1, value2 → result | 将两个长整型数相乘 | |
lneg | 75 | value → result | 对一个长整型数取反 | |
lookupswitch | ab | <0-3 字节填充>, defaultbyte1, defaultbyte2, defaultbyte3, defaultbyte4, npairs1, npairs2, npairs3, npairs4, 匹配偏移对... | 键 → | 使用键从表中查找目标地址,并从该地址处的指令开始继续执行 |
lor | 81 | value1, value2 → result | 两个长整型数的按位或运算 | |
lrem | 71 | value1, value2 → result | 两个长整型数相除的余数 | |
lreturn | ad | value → [empty] | 返回一个长整型值 | |
lshl | 79 | value1, value2 → result | 将长整型数 value1 按位左移 value2 位 | |
lshr | 7b | value1, value2 → result | 将长整型数 value1 按位右移 value2 位 | |
lstore | 37 | index | value → | 将长整型数 value 存储到局部变量 #index 中 |
lstore_0 | 3f | value → | 将长整型数 value 存储到局部变量 0 中 | |
lstore_1 | 40 | value → | 将长整型数 value 存储到局部变量 1 中 | |
lstore_2 | 41 | value → | 将长整型数 value 存储到局部变量 2 中 | |
lstore_3 | 42 | value → | 将长整型数 value 存储到局部变量 3 中 | |
lsub | 65 | value1, value2 → result | 将两个长整型数相减 | |
lushr | 7d | value1, value2 → result | 将长整型数 value1 按位右移 value2 位,无符号 | |
lxor | 83 | value1, value2 → result | 两个长整型数的按位异或运算 | |
M | ||||
monitorenter | c2 | objectref → | 进入对象的监视器(“获取锁” - 同步化()部分的开始) | |
monitorexit | c3 | objectref → | 退出对象的监视器(“释放锁” - 同步化()部分的结束) | |
multianewarray | c5 | indexbyte1, indexbyte2, 维度 | count1, [count2,...] → arrayref | 创建一个新的数组,该数组具有 维度 维度,元素类型由常量池中的类引用 index(indexbyte1 << 8 + indexbyte2)标识;每个维度的尺寸由 count1, [count2, 等] 标识 |
N | ||||
new | bb | indexbyte1, indexbyte2 | → objectref | 创建一个类型为常量池中的类引用 index(indexbyte1 << 8 + indexbyte2)标识的新对象 |
newarray | bc | atype | count → arrayref | 创建一个包含 count 个由 atype 标识的原始类型元素的新数组 |
nop | 00 | [无变化] | 不执行任何操作 | |
P | ||||
pop | 57 | value → | 丢弃堆栈顶部的值 | |
pop2 | 58 | {value2, value1} → | 丢弃堆栈顶部的两个值(如果它是双精度或长整型,则丢弃一个值) | |
putfield | b5 | indexbyte1, indexbyte2 | objectref, value → | 将 value 设置为对象 objectref 中的字段,其中字段由常量池中的字段引用 index(indexbyte1 << 8 + indexbyte2)标识 |
putstatic | b3 | indexbyte1, indexbyte2 | value → | 将 value 设置为类中的静态字段,其中字段由常量池中的字段引用 index(indexbyte1 << 8 + indexbyte2)标识 |
R | ||||
ret | a9 | index | [无变化] | 从局部变量 #index 中获取的地址处继续执行(与 jsr 的不对称性是故意的) |
return | b1 | → [空] | 从方法中返回 void | |
S | ||||
saload | 35 | arrayref, index → value | 从数组中加载短整型 | |
sastore | 56 | arrayref, index, value → | 将短整型存储到数组中 | |
sipush | 11 | byte1, byte2 | → value | 将一个带符号的整型数(byte1 << 8 + byte2)压入堆栈 |
swap | 5f | value2, value1 → value1, value2 | 交换堆栈顶部的两个字(注意 value1 和 value2 不能是双精度或长整型) | |
T | ||||
W | ||||
未用 | ||||
impdep1 | fe | 为调试器中的实现相关操作保留;不应出现在任何类文件中 | ||
impdep2 | ff | 为调试器中的实现相关操作保留;不应出现在任何类文件中 | ||
(无名称) | cb-fd | 这些值目前未分配给操作码,并为将来使用保留 | ||
xxxunusedxxx | ba | 此操作码“出于历史原因”保留 |
- 字节码可视化器 - 用于可视化和调试 Java 字节码的免费 Eclipse 插件
- ObjectWeb 为 Eclipse 提供的字节码大纲插件
- 填补 JVM、其目的以及与 JIT 编译的优势之间差距的简单说明