JVM字节码学习与理解
Contents
aconst_null
压特殊的 null 对象引用入栈。
iconst_
作用压整型数字0,1,2,3,4,5 入栈
注意
压入栈的指令还有:
bipush 0; sipush 0; idc 0;
但这些指令相对于等同的 iconst_
iconst_m1
将 -1 压入操作数栈
lconst_
lconst_0 或 lconst_1
压长整型 long integer 0 或 1 入栈
注意
也可以用
ldc2_w 0
尽管这条指令占用更多的字节以及效率更低
fconst_
fconst_0 或 fconst_1 或 fconst_2
压单精度浮点数 0.0, 1.0 或 2.0 入栈
注意
你也可以用
ldc 0.0
尽管这条指令占用更多的字节以及效率更低
dconst_
dconst_0 或 dconst_1
压双精度浮点数 0.0 或 1.0 入栈
注意
你也可以用
ldc2_w 0.0
尽管这条指令占用更多的字节以及效率更低
bipush
bipush
n 是一个 -128 <= n <= 127 的整数。压 1字节 有符号整数入栈。
注意
bipush 通常比 ldc 更高效。并且它在class文件中占用的字节数更少。
sipush
sipush
n 是一个 -32768 <= n <= 32767 的有符号整数。压 2字节 有符号整数入栈。
ldc
ldc
是一个int,或 floag,或 引号的 string。压一个字大小的常量入栈。
在字节码中, ldc 操作码后接着一个 8位 无符号的整数,这个整数是当前 class 的常量池项的索引。项是被标识为 CONSTRANT_Integer 或 CONSTRANT_Float 或 CONSTRANT_String 。
注意
当可能时,使用 bipush 或 sipush 或 const 指令之一来代替 ldc ,因为它们通常更高效。
ldc_w
ldc_w
是一个 int, 或 float 或 引号的 string。压一个字大小的常量入栈(wide index)
注意
ldc_w 与 ldc 是相同的。区别在于:
ldc_w 使用的是 16-bit 索引,而 ldc 使用的是 8-bit 索引
ldc2_w
ldc2_w
是一个 long integer 或 double 数字。
在字节码中, ldc2_w 操作码后接着一个 16-bit 无符号的整数。这个整数是一个常量池中的索引项。项是标识为 CONSTRANT_Double 或 CONSTRANT_Long 的项。
iload
iload
从本地变量 (local variable) (varnum是本地变量的号码) 获取 integer。即将本地变量 varnum 的值压入栈。
iload_
它等同于 iload
lload
lload
从本地变量(local variable) 获取一个 long integer,然后将它压入栈
lload_
它等同于 lload
注意
由于 long integer 是 64-bit 的,并且每一个本地变量最多持有32-bit,所以Java使用了连续2个本地变量,
fload
fload
从本地变量 (local variable) (varnum是本地变量的号码) 获取 float。即将本地变量 varnum 的值压入栈。
fload_
它等同于 fload
dload
dload
从本地变量 (local variable) (varnum是本地变量的号码) 获取 double。即将本地变量 varnum 的值压入栈。
注意
由于 double 是 64-bit 的,并且每一个本地变量最多持有32-bit,所以Java使用了连续2个本地变量
因此, dload
dload_
它等同于 dload
aload
aload
从本地变量(local variable)获取一个对象的引用,然后将它压入栈。
aload_
它等同于 aload
aaload
从一个对象数组(array of objects)中获取一个对象引用(object reference),然后将它压入栈
aaload 之前,栈顶应该为:
index:数组索引 arrayref:数组对象的引用
aaload 之后,栈顶为:
arrayref[index]的值
所以,aaload之前,通常要准备好arrayref 和 index 的值压入栈。
iaload
从一个int数组(integer array)中获取一个int,然后将它压入栈。
laload
从一个long数组(long array)中获取一个long,然后将它压入栈。
daload
从一个double数组(double array)中获取一个double,然后将它压入栈。
baload
从一个字节数组(byte array)中获取一个字节,然后扩展它为一个 integer ,最后压它入栈。
它也用作从布尔数组中接收值。(Sun的实现中,boolean array 通常也是保存为 byte array的,每一个字节代表一个布尔值)
caload
从一个字符数组中获取一个字符,然后将它压入栈。
saload
从一个 short 数组中获取一个 short 。
istore
istore
从栈顶弹出(pop)一个int,并且将它保存到本地变量 (local variable)
中。
istore_
istore_0 , istore_1 , istore_2 ,istore_3
它等同于 istore
lstore
lstore
从栈顶弹出(pop)一个int,并且将它保存到本地变量 (local variable)
中。
注意
long 是 64-bit 的,并且每个本地变量最多只能拥有 32-bit,由于Java使用2个连续的本地变量
因此, lstore
lstore_
lstore_0, lstore_1, lstore_1, lstore_2, lstore_3
它等同于 lstore
fstore
fstore
从栈顶弹出(pop)一个float,并且将它保存到本地变量 (local variable)
中。
fstore_
fstore_0, fstore_1, fstore_1, fstore_2, fstore_3
它等同于 fstore
dstore
dstore
从栈顶弹出(pop)一个double,并且将它保存到本地变量 (local variable)
中。
注意
double 是 64-bit 的,并且每个本地变量最多只能拥有 32-bit,由于Java使用2个连续的本地变量
因此, dstore
dstore_
dstore_0, dstore_1, dstore_1, dstore_2, dstore_3
它等同于 dstore
astore
astore
从栈顶弹出(pop)一个对象引用,并且将它保存到本地变量 (local variable)
中。
astore_
astore_0, astore_1, astore_1, astore_2, astore_3
它等同于 astore
iastore
从栈中获取一个 int,并且将它保存到一个 int 数组对象中。
iastore 指令执行之前的栈: value index arrayref xxx
执行完之后: xxx
lastore
从栈中获取一个双字,long integer ,并且将它保存到一个 long integer 数组中。
lastore 指令执行之前的栈: value-word1 value-word2 index arrayref xxx
执行完之后: xxx
fastore
与 iastore 类似
dastore
与 lastore 类似
aastore
与 iastore 类似
castore
与 iastore 类似。 c 表示 character
sastore
与 iastore 类似. s 表示 short
pop
从栈顶中丢弃一个字(single-word)大小的数据。
pop2
从栈顶中丢弃两个字(two words)大小的数据。
dup
复制栈顶一个字(single-word)的数据,然后压它入栈。
即 pop 一次,push 两次
dup_x1
复制栈顶一个字(single-word)的数据,然后插入到原栈顶下面第二项中。
执行指令之前,栈顶: word1 word2 xxxx
执行完指令之后: word1 word2 word1 xxxx
dup_x2
复制栈顶一个字(single-word)的数据,然后插入到原栈顶下面第三项中。
执行指令之前,栈顶: word1 word2 word3 xxxx
执行完指令之后: word1 word2 word3 word1 xxxx
dup2
复制栈顶两个字(two-words)的数据,然后压它入栈。(比如2个int, 1个int和object引用,又或者是一个 double ,或一个 long)
dup2_x1
复制栈顶两个字(two-words)的数据,然后将复制的那个字(two-word)插入到原前一个字(previous single word)前面。
比如 dup2_x1 指令之前的栈为: word1 word2 word3 xxxx
执行完指令之后,栈为: word1 word2 word3 word1 word2 xxxx
dup2_x2
复制栈顶两个字(two-words)的数据,然后将复制的那个字(two-word)插入到原前两个字(previous two word)前面。
比如 dup2_x1 指令之前的栈为: word1 word2 word3 word4 xxxx
执行完指令之后,栈为: word1 word2 word3 word4 word1 word2 xxxx
swap
交换栈顶两个字的数据
指令执行之前: word1 word2 xxxx
指令执行完后: word2 word1 xxxx
iadd, isub, imul, idiv, irem
将栈顶两个数弹出(pop),然后将它们相加(减,乘,除,取模)的结果压入栈
ladd,lsub, lmul, ldiv, lrem
将栈顶两个 long integer 弹出,然后将它们相加(减,乘,除,取模)的结果压入栈。
指令前的栈: value1-word1 value1-word2 value2-word1 value2-word2 xxxx
指令执行后的栈: result-word1 result-word2 xxxx
fadd, fsub, fmul, fdiv, frem
将栈顶两个单精度浮点数弹出,然后将它们相加(减,乘,除,取模)的结果压入栈。
dadd, dsub, dmul, ddiv, drem
将栈顶两个双精度浮点数弹出,然后将它们相加(减,乘,除,取模)的结果压入栈。
指令前的栈: value1-word1 value1-word2 value2-word1 value2-word2 xxxx
指令后的栈: result-word1 result-word2 xxxx
ineg, lneg, fneg, dneg
将栈顶 int, long, float, double 弹出,取负后,再压入栈
ishl, lshl, ishr, lshr
将 int, long 左移或右移指定位数(有符号)。
栈前: value1 => 要位移的个数 value2 => 要进行位移的数 xxxx
栈后 result xxxx
例如, ishr ,它等同于 x / (2^n) ,n 就是 value1,x 就是 value2
iushr, lushr
它是无符号位移
iand, land, ior, lor, ixor, lxor
将栈顶两个 int 或 两个long 的值进行位与(或、异或)操作。
栈前: value1 value2 xxxx
栈后 result xxxx
iinc
iinc
表示将本地变量 (local variable) 序号为 varnum 的变量,增加 n (注意,n可以为负的,即减的意义)
i2l
将 int 转换为 long 。
栈前: int xxxx
栈后 long-word1 long-word2 xxxx
i2f
栈前
int xxxx
栈后
float xxxx
i2d
栈前 int xxxx
栈后 double-word1 double-word2 xxxx
l2i
栈前
long-word1 long-word2 xxxx
栈后
int xxxx
l2f
栈前 long-word1 long-word2 xxxx
栈后 float xxxx
l2d
栈前
long-word1 long-word2 xxxx
栈后
double-word1 double-word2 xxxx
f2i
栈前
float xxxx
栈后
int xxxx
f2l
栈前
float xxxx
栈后
long-word1 long-word2 xxxx
f2d
栈前
float xxxx
栈后
double-word1 double-word2 xxxx
d2i
栈前
double-word1 double-word2 xxxx
栈后
int xxxx
d2l
栈前
double-word1 double-word2 xxxx
栈后
long-world1 long-world2
d2f
栈前
double-word1 double-word2 xxxx
栈后
float xxxx
i2b
从栈顶中弹出一个 int,然后转换为有符号的字节(前8位保留,后24位丢弃,即后24位设置为0),然后将结果进行有符号扩展,最后将结果压入栈
注意:它可能会导致符号(正负)改变。
i2c
从栈顶中弹出一个 int,然后转换为一个 16位无符号字符。(后16位设置为0),然后将结果压入栈
i2s
从栈顶中弹出一个 int,然后转换为一个有符号的 short 。(后16位设置为0),然后将结果进行有符号扩展,最后将结果压入栈。
注意:它可能会导致符号(正负)改变。
lcmp
将栈顶两个 long 值弹出,然后比较它们的大小。如果相等,则将 0 压入栈。如果 value2 > value 1 ,则将 1 压入栈;如果 value1 > value 2 ,则将 -1 压入栈。
栈前: value1-word1 value1-word2 value2-word1 value2-word2 xxxx
栈后: int-result xxxx
fcmpl
单精度浮点数比较。(如果任一数字是 NaN,则压-1入栈)
栈前 value1 value2 xxxx
栈后 int-result xxxx
如果相等,则将 int 0 压入栈。如果 value2 > value 1 ,则将 1 压入栈; 如果 value1 > value2,则将 -1 压入栈。
fcmpg
与 fcmpl 相同。但如果任一数字是 NaN,则压1入栈。
dcmpl
双精度浮点比如(如果任一数字是 NaN,则压-1入栈)
栈前: value1-word1 value1-word2 value2-word1 value2-word2 xxxx
栈后 int-result xxxx
如果相等,则将 int 0 压入栈。如果 value2 > value 1 ,则将 1 压入栈; 如果 value1 > value2,则将 -1 压入栈。
dcmpg
与 dcmpl 相同。但如果任一数字是 NaN,则压1入栈。
ifeq
ifeq
如果栈顶的 int 为0,则跳转到 label 的位置。(弹出栈顶)
ifne
ifne
如果栈顶的 int 不为0,则跳转到 label 的位置。(弹出栈顶)
ifge
ifge
如果栈顶的 int 大于等于 0,则跳转到 label 的位置。(弹出栈顶)
ifgt
ifgt
如果栈顶的 int 大于 0,则跳转到 label 的位置。(弹出栈顶)
ifle
ifle
如果栈顶的 int 小于等于 0,则跳转到 label 的位置。(弹出栈顶)
if 与 cmp 指令结合
它们都是以 int 来比较(弹出栈顶两个 int)
if_icmpeq => 比较,如果等于0(即相等),则跳转 if_icmpne if_icmplt if_icmpge if_icmpgt if_icmple
if_acmpeq => 比较两个引用型,如果相等,则跳转 if_acmpne => 比较两个引用型,如果不相等,则跳转
goto
goto
跳转到 label
jsr
jump to subroutine
jsr
栈前: xxx
栈后 return-address xxx
它通常用来实现 Java 中的 finally 子句。
ret
ret
它用于从 subroutine (即用 jsr 指令调用的)返回。
tableswitch
用于执行计算比较跳转
tableswitch
栈前: val xxxx
栈后 xxxx
low 是一个有符号 32-bit 整数。
lookupswitch
用于执行高效的 比较-然后-跳转。通常是 switch 语句。
lookupswitch
key1, key2 都是 32-bit 整数
栈前: item xxxx
栈后 xxxx
ireturn
从方法中返回 integer 结果。
栈前: return-value xxxx
栈后 xxxx
它从栈顶中弹出一个 int ,然后将它压入调用者(例如,使用 invokevirtual, invokespecial, invokestatic or invokeinterface 来调用当前正执行的方法)的操作数栈中。当前正执行的方法的其他操作数栈都会被丢弃。
lreturn
与 ireturn 相同。只是它返回的是 long (注意,它要占用2个32-bit)
freturn
与 ireturn 相同。只是它返回的是 float
dreturn
与 ireturn 相同。只是它返回的是 double
areturn
与 ireturn 相同。只是它返回的是 对象引用.
栈前: objectref xxxx
栈后 xxxx
return
从方法中返回。方法的返回类型为 void。
getstatic
获取静态域 static field
getstatic
它会从栈顶中的 objectref 引用弹出,然后从 objectref 中获取以 field-spec 标识的静态域,最后将这个值压入栈(一个字或双字,双字的情况,比如 double, long 类型)
栈前: xxxx
栈后 value xxxx
或 value-word1 value-word2 xxxx
putstatic
putstatic
为静态域设置值(值是从栈顶中的一个字或双字获取)
栈前: value xxxx
栈后 xxxx
或
栈前: value-word1 value-word2 xxxx
栈后: xxxx
getfield
getfield
例如: getfield java/lang/System/out Ljava/io/PrintStream;
从栈顶中弹出对象的引用(objectref),然后获取它的指定字段,最后将字段的值(一个字或双字)压入栈。
栈前: objectref xxxx
栈后 value xxxx
或
栈前: objectref xxxx
栈后: value-word1 value-word2 xxxx
putfield
putfield
为实例对象设置值。
栈前: value objectref xxxx
栈后: xxxx
或
栈前: value-word1 value-word2 objectref xxxx
栈后: xxxx
invokevirtual
调用实例方法
invokevirtual
栈前: arg1 arg2 … argN objectref xxxx
栈后: result xxxx
invokespecial
invokespecial
调用属于指定 class 的方法(比如构造函数, this 的私有方法, this 父类的方法等)
栈前:
argN … arg2 arg1 objectref xxxx
栈后: [result] xxxx
invokestatic
invokestatic
调用一个类的静态方法
栈前: argN … arg2 arg1 objectref xxxx
栈后: [result] xxxx
invokeinterface
调用一个接口方法
invokeinterface
栈前: argN … arg2 arg1 objectref xxxx
栈后: [result] xxxx
new
new
创建一个对象
栈前: xxxx
栈后: objectref xxxx
newarray
newarray
type 可以是以下之一: boolean, char, float, double, byte, short, int, long
栈前: n xxxx
栈后: arrayref xxxx
anewarray
anewarray
type 是一个 class 或 interface 名。
栈前: size xxxx
栈后: arrayref xxxx
arraylength
获取数组长度
栈前: arrayref xxxx
栈后: length xxxx
athrow
抛出一个异常。
栈前: objectref[这是一个异常类型对象, throwable 或它的子类] xxxx
栈后: xxxx
checkcast
检查对象或数组的类型
checkcast
栈前: objectref xxxx
栈后: objectref xxxx
检查栈顶的操作数(一个对象或数组的引用)是否可以转换为指定的类型。
instanceof
instanceof
测试一个对象是否某个类的实例
栈前: objectref xxxx
栈后: int-result xxxx
如果是,则int-result为1,否则为0
monitorenter
进入一个同步的代码区域
栈前: objectref xxxx
栈后: xxxx
monitorexit
离开一个同步的代码区域
栈前: objectref xxxx
栈后: xxxx
nop
这个条指令并不会做任何事。编译器有时会有了调试,测试或时序的目的而生成 nop