0%

jvm 字节码指令

概述

java 虚拟机的指令由一个操作码(Opcode)以及零或多个操作数(Operands)组成。其中,操作码是一个字节长度的数字,不同的数字代表不同的操作码。对于操作数而言,一个字节码的数字代表一个操作数。由于 java 虚拟机采用面向操作数栈而不是寄存器的架构,因此,大多数的指令都不包含操作数,只有一个操作码。

在 java 虚拟机的指令集中,大多数的指令都包含了其操作所对应的数据类型信息,在这些指令中,操作码中第一个字母表明了其数据类型:i 代表 int,l 代表 long,s 代表 short,b 代表 byte,c 代表 char,f 代表 float,d 代表 double,a 代表 reference。

由于操作码只占一个字节,所以最多只有 256 个指令,这也决定了 java 虚拟机中只对一些常用的操作提供了有限的类型相关识别指令,对于剩下的部分,java 虚拟机通过类型转换将其他类型转成虚拟机指令支持的类型然而再进行操作。

由于虚拟机指令很多,而且部分指令功能类似,所以本文只介绍部分常用的指令。

加载指令

加载指令是指将数据在栈帧中的局部变量表加载到操作数栈中。其主要的指令包括两种:将一个局部变量加载到操作数栈和将一个常量加载到操作数栈。

加载局部变量

部分常见的加载局部变量的指令如下所示

助记符 操作数 含义
iload 一个,i 加载第 i 个 int 类型的局部变量,i 从 0 开始计数
iload_0 0 加载第 0 个 int 类型的局部变量,与 iload 0 指令相同
iload_1 加载第 1 个 int 类型的局部变量,与 iload 1 指令相同
iload_2 加载第 2 个 int 类型的局部变量,与 iload 2 指令相同
iload_3 加载第 3 个 int 类型的局部变量,与 iload 3 指令相同
lload 一个,i 加载第 i 个 long 类型的局部变量,i 从 0 开始计数
lload_0 加载第 0 个 long 类型的局部变量,与 lload 0 指令相同
lload_1 加载第 1 个 long 类型的局部变量,与 lload 1 指令相同
lload_2 加载第 2 个 long 类型的局部变量,与 lload 2 指令相同
lload_3 加载第 3 个 long 类型的局部变量,与 lload 3 指令相同
fload 一个,i 加载第 i 个 float 类型的局部变量,i 从 0 开始计数
fload_0 加载第 0 个 float 类型的局部变量,与 fload 0 指令相同
fload_1 加载第 1 个 float 类型的局部变量,与 fload 1 指令相同
fload_2 加载第 2 个 float 类型的局部变量,与 fload 2 指令相同
fload_3 加载第 3 个 float 类型的局部变量,与 fload 3 指令相同
dload 一个,i 加载第 i 个 double 类型的局部变量,i 从 0 开始计数
dload_0 加载第 0 个 double 类型的局部变量,与 dload 0 指令相同
dload_1 加载第 1 个 double 类型的局部变量,与 dload 1 指令相同
dload_2 加载第 2 个 double 类型的局部变量,与 dload 2 指令相同
dload_3 加载第 3 个 double 类型的局部变量,与 dload 3 指令相同
aload 一个,i 加载第 i 个 引用类型的局部变量,i 从 0 开始计数
aload_0 加载第 0 个 引用类型的局部变量,与 aload 0 指令相同
aload_1 加载第 1 个 引用类型的局部变量,与 aload 1 指令相同
aload_2 加载第 2 个 引用类型的局部变量,与 aload 2 指令相同
aload_3 加载第 3 个 引用类型的局部变量,与 aload 3 指令相同

加载常量

部分常见的加载常量的指令如下所示

操作码 操作数 含义
bipush 一个,i 将单字节的常量值 i (-128 ~ 127)推送到栈顶
sipush 两个,byte1, byte2 将 byte1 和 byte2 进行 ((byte1 << 8)&#124; byte2) 运算,得到 short 类型(-32768 ~ 32767),并将其推送到栈顶
ldc 一个,i 与 ldc_w 类似,将常量池中第 i 个(从 1 开始计数)常量的值取出推送到栈顶,类型可以是 int、float、String。当常量池中常量数量不超过 255 个时,使用此指令,否则使用 ldc_w
ldc_w 两个,indexbyte1,indexbyte2 与 ldc 类似,将常量池中第 ((indexbyte1 << 8) &#124; indexbyte2) 个(从 1 开始计数)常量的值取出推送到栈顶,类型可以是 int、float、String。当常量池中常量数量超过 255 个时,使用此指令,否则使用 ldc
ldc2_w 两个,indexbyte1,indexbyte2 将常量池中第 ((indexbyte1 << 8) &#124; indexbyte2) 个(从 1 开始计数)常量的值取出推送到栈顶,类型可以是 long 或 double
aconst_null 将一个 null 推送到栈顶
iconst_m1 将数字 -1(int 类型)推送到栈顶
iconst_0 将数字 0(int 类型)推送到栈顶
iconst_1 将数字 1(int 类型)推送到栈顶
iconst_2 将数字 2(int 类型)推送到栈顶
iconst_3 将数字 3(int 类型)推送到栈顶
iconst_4 将数字 4(int 类型)推送到栈顶
iconst_5 将数字 5(int 类型)推送到栈顶
lconst_0 将数字 0(long 类型)推送到栈顶
lconst_1 将数字 1(long 类型)推送到栈顶
fconst_0 将数字 0(float 类型)推送到栈顶
fconst_1 将数字 1(float 类型)推送到栈顶
fconst_2 将数字 2(float 类型)推送到栈顶
dconst_0 将数字 0(double 类型)推送到栈顶
dconst_1 将数字 1(double 类型)推送到栈顶

存储指令

存储指令主要是将一个数值从操作数栈存储到局部变量表中,常见的指令如下

助记符 操作数 含义
istore 一个,i 将栈顶 int 类型数值存入第 i 个(从 0 开始计数)本地局部变量
istore_0 将栈顶 int 类型的数值存入第 0 个本地局部变量,与 istore 0 指令相同
istore_1 将栈顶 int 类型的数值存入第 1 个本地局部变量,与 istore 1 指令相同
istore_2 将栈顶 int 类型的数值存入第 2 个本地局部变量,与 istore 2 指令相同
istore_3 将栈顶 int 类型的数值存入第 3 个本地局部变量,与 istore 3 指令相同
lstore 一个,i 将栈顶 long 类型数值存入第 i 个(从 0 开始计数)本地局部变量
lstore_0 将栈顶 long 类型的数值存入第 0 个本地局部变量,与 lstore 0 指令相同
lstore_1 将栈顶 long 类型的数值存入第 1 个本地局部变量,与 lstore 1 指令相同
lstore_2 将栈顶 long 类型的数值存入第 2 个本地局部变量,与 lstore 2 指令相同
lstore_3 将栈顶 long 类型的数值存入第 3 个本地局部变量,与 lstore 3 指令相同
fstore 一个,i 将栈顶 float 类型数值存入第 i 个(从 0 开始计数)本地局部变量
fstore_0 将栈顶 float 类型的数值存入第 0 个本地局部变量,与 fstore 0 指令相同
fstore_1 将栈顶 float 类型的数值存入第 1 个本地局部变量,与 fstore 1 指令相同
fstore_2 将栈顶 float 类型的数值存入第 2 个本地局部变量,与 fstore 2 指令相同
fstore_3 将栈顶 float 类型的数值存入第 3 个本地局部变量,与 fstore 3 指令相同
dstore 一个,i 将栈顶 double 类型数值存入第 i 个(从 0 开始计数)本地局部变量
dstore_0 将栈顶 double 类型的数值存入第 0 个本地局部变量,与 dstore 0 指令相同
dstore_1 将栈顶 double 类型的数值存入第 1 个本地局部变量,与 dstore 1 指令相同
dstore_2 将栈顶 double 类型的数值存入第 2 个本地局部变量,与 dstore 2 指令相同
dstore_3 将栈顶 double 类型的数值存入第 3 个本地局部变量,与 dstore 3 指令相同
astore 一个,i 将栈顶引用类型数值存入第 i 个(从 0 开始计数)本地局部变量
astore_0 将栈顶引用类型的数值存入第 0 个本地局部变量,与 astore 0 指令相同
astore_1 将栈顶引用类型的数值存入第 1 个本地局部变量,与 astore 1 指令相同
astore_2 将栈顶引用类型的数值存入第 2 个本地局部变量,与 astore 2 指令相同
astore_3 将栈顶引用类型的数值存入第 3 个本地局部变量,与 astore 3 指令相同

运算指令

运算指令用于对两个操作数栈上的数值进行某种特定的运算,并将结果重新存入到操作数栈中。运算指令包括:加法指令、减法指令、乘法指令、除法指令、求余指令、取反指令、位移指令、按位或指令、按位与指令、按位异或指令、局部变量自增指令、比较指令。

加法指令

助记符 操作数 含义
iadd 将栈顶两个 int 类型的数值出栈,对其求和,并将结果压入栈中
ladd 将栈顶两个 long 类型的数值出栈,对其求和,并将结果压入栈中
fadd 将栈顶两个 float 类型的数值出栈,对其求和,并将结果压入栈中
dadd 将栈顶两个 double 类型的数值出栈,对其求和,并将结果压入栈中

减法指令

助记符 操作数 含义
isub 将栈顶两个 int 类型的数值出栈,以第一个栈顶元素为减数,第二个栈顶元素为被减数,对其求差,并将结果压入栈中
lsub 将栈顶两个 long 类型的数值出栈,以第一个栈顶元素为减数,第二个栈顶元素为被减数,对其求差,并将结果压入栈中
fsub 将栈顶两个 float 类型的数值出栈,以第一个栈顶元素为减数,第二个栈顶元素为被减数,对其求差,并将结果压入栈中
dsub 将栈顶两个 double 类型的数值出栈,以第一个栈顶元素为减数,第二个栈顶元素为被减数,对其求差,并将结果压入栈中

乘法指令

助记符 操作数 含义
imul 将栈顶两个 int 类型的数值出栈,对其求积,并将结果压入栈中
lmul 将栈顶两个 long 类型的数值出栈,对其求积,并将结果压入栈中
fmul 将栈顶两个 float 类型的数值出栈,对其求积,并将结果压入栈中
dmul 将栈顶两个 double 类型的数值出栈,对其求积,并将结果压入栈中

除法指令

助记符 操作数 含义
idiv 将栈顶两个 int 类型的数值出栈,以第一个栈顶元素为除数,第二个栈顶元素为被除数,对其求商,并将结果压入栈中
ldiv 将栈顶两个 long 类型的数值出栈,以第一个栈顶元素为除数,第二个栈顶元素为被除数,对其求商,并将结果压入栈中
fdiv 将栈顶两个 float 类型的数值出栈,以第一个栈顶元素为除数,第二个栈顶元素为被除数,对其求商,并将结果压入栈中
ddiv 将栈顶两个 double 类型的数值出栈,以第一个栈顶元素为除数,第二个栈顶元素为被除数,对其求商,并将结果压入栈中

求余指令

助记符 操作数 含义
irem 将栈顶两个 int 类型的数值出栈,以第一个栈顶元素为除数,第二个栈顶元素为被除数,对其求余,并将结果压入栈中
lrem 将栈顶两个 long 类型的数值出栈,以第一个栈顶元素为除数,第二个栈顶元素为被除数,对其求余,并将结果压入栈中
frem 将栈顶两个 float 类型的数值出栈,以第一个栈顶元素为除数,第二个栈顶元素为被除数,对其求余,并将结果压入栈中
drem 将栈顶两个 double 类型的数值出栈,以第一个栈顶元素为除数,第二个栈顶元素为被除数,对其求余,并将结果压入栈中

取反指令

助记符 操作数 含义
ineg 将栈顶 int 类型的数值出栈,取反,并将结果压入栈中
lneg 将栈顶 long 类型的数值出栈,取反,并将结果压入栈中
fneg 将栈顶 float 类型的数值出栈,取反,并将结果压入栈中
dneg 将栈顶 double 类型的数值出栈,取反,并将结果压入栈中

位移指令

助记符 操作数 含义
ishl 将栈顶两个 int 类型的数值出栈,以第一个栈顶元素为移动位数,第二个栈顶元素为位移数,进行左位移操作,并将结果压入栈中
lshl 将栈顶两个 long 类型的数值出栈,以第一个栈顶元素为移动位数,第二个栈顶元素为位移数,进行左位移操作,并将结果压入栈中
ishr 将栈顶两个 int 类型的数值出栈,以第一个栈顶元素为移动位数,第二个栈顶元素为位移数,进行带符号右位移操作,并将结果压入栈中
lshr 将栈顶两个 long 类型的数值出栈,以第一个栈顶元素为移动位数,第二个栈顶元素为位移数,进行带符号右位移操作,并将结果压入栈中
iushr 将栈顶两个 int 类型的数值出栈,以第一个栈顶元素为移动位数,第二个栈顶元素为位移数,进行无符号右位移操作,并将结果压入栈中
lushr 将栈顶两个 long 类型的数值出栈,以第一个栈顶元素为移动位数,第二个栈顶元素为位移数,进行无符号右位移操作,并将结果压入栈中

按位或指令

助记符 操作数 含义
ior 将栈顶两个 int 类型的数值出栈,对其进行按位或运算,并将结果压入栈中
lor 将栈顶两个 long 类型的数值出栈,对其进行按位或运算,并将结果压入栈中

按位与指令

助记符 操作数 含义
iand 将栈顶两个 int 类型的数值出栈,对其进行按位与运算,并将结果压入栈中
land 将栈顶两个 long 类型的数值出栈,对其进行按位与运算,并将结果压入栈中

按位异或指令

助记符 操作数 含义
ixor 将栈顶两个 int 类型的数值出栈,对其进行按位异或运算,并将结果压入栈中
lxor 将栈顶两个 long 类型的数值出栈,对其进行按位异或运算,并将结果压入栈中

局部变量自增指令

助记符 操作数 含义
iinc 两个,index 和 const 将第 index 个(index 从 0 开始计算)局部变量的数值加上 const 常量数值得到结果

比较指令

助记符 操作数 含义
lcmp 将栈顶两个 long 类型的数值出栈,以第一个栈顶元素为 value2,第二个栈顶元素为value1,如果 value1 - value2 > 0,则将 1 压入栈中,如果 value1 - value2 = 0,则将 0 压入栈中,如果 value1 - value2 < 0,则将 -1 压入栈中
fcmpl 将栈顶两个 float 类型的数值出栈,以第一个栈顶元素为 value2,第二个栈顶元素为value1,如果 value1 和 value2 中有一个值为 NaN,则将 -1 压如栈中;如果 value1 - value2 > 0,则将 1 压入栈中,如果 value1 - value2 = 0,则将 0 压入栈中,如果 value1 - value2 < 0,则将 -1 压入栈中
fcmpg 将栈顶两个 float 类型的数值出栈,以第一个栈顶元素为 value2,第二个栈顶元素为value1,如果 value1 和 value2 中有一个值为 NaN,则将 1 压如栈中;如果 value1 - value2 > 0,则将 1 压入栈中,如果 value1 - value2 = 0,则将 0 压入栈中,如果 value1 - value2 < 0,则将 -1 压入栈中
dcmpl 将栈顶两个 double 类型的数值出栈,以第一个栈顶元素为 value2,第二个栈顶元素为value1,如果 value1 和 value2 中有一个值为 NaN,则将 -1 压如栈中;如果 value1 - value2 > 0,则将 1 压入栈中,如果 value1 - value2 = 0,则将 0 压入栈中,如果 value1 - value2 < 0,则将 -1 压入栈中
dcmpg 将栈顶两个 double 类型的数值出栈,以第一个栈顶元素为 value2,第二个栈顶元素为value1,如果 value1 和 value2 中有一个值为 NaN,则将 1 压如栈中;如果 value1 - value2 > 0,则将 1 压入栈中,如果 value1 - value2 = 0,则将 0 压入栈中,如果 value1 - value2 < 0,则将 -1 压入栈中

类型转换指令

java 虚拟机直接支持数值类型的宽化类型转换指令(即小范围类型向大范围类型的安全转换),这些转换无需显示的转换指令,当然,也可以用转换指令来要求其进行显示的转换。其主要包括以下三种类型

  • int 类型转换到 long、float 或者 double 类型
  • long 类型转换到 float、double 类型
  • float 类型转换到 double 类型

对于窄转换类型(即大范围类型向小范围类型的安全转换),必须显示使用转换指令来完成,主要包括以下指令

助记符 操作数 含义
i2b 将栈顶 int 类型数值出栈,将其带符号转换成 byte 类型数据,并将其结果压入操作数栈。注意:此过程可能导致精度丢失,甚至可能导致转换结果与原先数值有不同的正负号
i2c 将栈顶 int 类型数值出栈,将其带符号转换成 char 类型数据,并将其结果压入操作数栈。注意:此过程可能导致精度丢失,甚至可能导致转换结果与原先数值有不同的正负号
i2s 将栈顶 int 类型数值出栈,将其带符号转换成 short 类型数据,并将其结果压入操作数栈。注意:此过程可能导致精度丢失,甚至可能导致转换结果与原先数值有不同的正负号
l2i 将栈顶 long 类型数值出栈,将其带符号转换成 int 类型数据,并将其结果压入操作数栈。注意:此过程可能导致精度丢失,甚至可能导致转换结果与原先数值有不同的正负号
l2f 将栈顶 long 类型数值出栈,将其带符号转换成 int 类型数据,并将其结果压入操作数栈。注意:此过程可能导致精度丢失,甚至可能导致转换结果与原先数值有不同的正负号
l2d 将栈顶 long 类型数值出栈,将其带符号转换成 double 类型数据,并将其结果压入操作数栈。注意:此过程可能导致精度丢失,甚至可能导致转换结果与原先数值有不同的正负号
f2i 将栈顶 float 类型数值出栈,将其带符号转换成 int 类型数据,并将其结果压入操作数栈。注意:此过程可能导致精度丢失,甚至可能导致转换结果与原先数值有不同的正负号
f2l 将栈顶 float 类型数值出栈,将其带符号转换成 long 类型数据,并将其结果压入操作数栈。注意:此过程可能导致精度丢失,甚至可能导致转换结果与原先数值有不同的正负号
f2d 将栈顶 float 类型数值出栈,将其带符号转换成 double 类型数据,并将其结果压入操作数栈。注意:此过程可能导致精度丢失,甚至可能导致转换结果与原先数值有不同的正负号
d2i 将栈顶 double 类型数值出栈,将其带符号转换成 int 类型数据,并将其结果压入操作数栈。注意:此过程可能导致精度丢失,甚至可能导致转换结果与原先数值有不同的正负号
d2l 将栈顶 double 类型数值出栈,将其带符号转换成 long 类型数据,并将其结果压入操作数栈。注意:此过程可能导致精度丢失,甚至可能导致转换结果与原先数值有不同的正负号
d2f 将栈顶 double 类型数值出栈,将其带符号转换成 float 类型数据,并将其结果压入操作数栈。注意:此过程可能导致精度丢失,甚至可能导致转换结果与原先数值有不同的正负号

对象创建与访问指令

助记符 操作数 含义
new 两个,indexbyte1, indexbyte2 根据常量池中第 ((indexbyte1 << 8) &#124; indexbyte2) 个(从 1 开始计数)常量索引指向的类创建该类实例,并将类引用压入栈顶
newarray 一个,atype 根据 atype 确定要创建的基本数据类型,其中 atype的值表示如下:4 表示 boolean,5 表示 char,6 表示 float,7 表示 double,8 表示 byte,9 表示 short,10 表示 int,11 表示 long。然后将栈顶 int 类型的数值出栈,表示创建数组的大小,创建数组完毕后,将数组的引用值压入操作数栈中
anewarray 两个,indexbyte1, indexbyte2 根据常量池中第 ((indexbyte1 << 8) &#124; indexbyte2) 个(从 1 开始计数)常量索引指向的类确定创建数组的类型。然后将栈顶 int 类型的数值出栈,表示创建数组的大小,创建数组完毕后,将数组的引用值压入操作数栈中
getfield 两个,indexbyte1, indexbyte2 根据常量池中第 ((indexbyte1 << 8) &#124; indexbyte2) 个(从 1 开始计数)常量索引指向的字段确定具体的字段描述信息,然后将栈顶的对象引用数值出栈,根据对象引用和字段描述信息获取该字段的具体值,然后压入栈顶
putfield 两个,indexbyte1, indexbyte2 根据常量池中第 ((indexbyte1 << 8) &#124; indexbyte2) 个(从 1 开始计数)常量索引指向的字段确定具体的字段描述信息,然后将栈顶的数值 value 出栈,再将栈顶的对象引用 objectref 出栈,根据对象引用和字段描述信息获取该字段并将其设置为 value 值
getstatic 两个,indexbyte1, indexbyte2 根据常量池中第 ((indexbyte1 << 8) &#124; indexbyte2) 个(从 1 开始计数)常量索引指向的字段获取该字段的具体值,然后压入栈顶
putstatic 两个,indexbyte1, indexbyte2 根据常量池中第 ((indexbyte1 << 8) &#124; indexbyte2) 个(从 1 开始计数)常量索引指向的字段确定具体的字段描述信息,然后将栈顶的数值 value 出栈,根据字段描述信息获取该字段并将其设置为 value 值
baload 将栈顶的 int 类型数值 index 出栈(表示数组的下标),再将栈顶的 boolean 或 byte 类型的数组引用类型 arrayref 出栈,根据 arrayref 和 index 定位到具体的数组元素,将其具体值压入栈顶
caload 将栈顶的 int 类型数值 index 出栈(表示数组的下标),再将栈顶的 char 类型的数组引用类型 arrayref 出栈,根据 arrayref 和 index 定位到具体的数组元素,将其具体值压入栈顶
iaload 将栈顶的 int 类型数值 index 出栈(表示数组的下标),再将栈顶的 int 类型的数组引用类型 arrayref 出栈,根据 arrayref 和 index 定位到具体的数组元素,将其具体值压入栈顶
daload 将栈顶的 int 类型数值 index 出栈(表示数组的下标),再将栈顶的 double 类型的数组引用类型 arrayref 出栈,根据 arrayref 和 index 定位到具体的数组元素,将其具体值压入栈顶
aaload 将栈顶的 int 类型数值 index 出栈(表示数组的下标),再将栈顶的对象类型的数组引用类型 arrayref 出栈,根据 arrayref 和 index 定位到具体的数组元素,将其具体值压入栈顶
bastore 将栈顶的 value 数值出栈,再将栈顶的 int 类型数值 index 出栈(表示数组的下标),再将栈顶的 boolean 或 byte 类型的数组引用类型 arrayref 出栈,根据 arrayref 和 index 定位到具体的数组元素,将该元素的值赋为 value 值
castore 将栈顶的 value 数值出栈,再将栈顶的 int 类型数值 index 出栈(表示数组的下标),再将栈顶的 char 类型的数组引用类型 arrayref 出栈,根据 arrayref 和 index 定位到具体的数组元素,将该元素的值赋为 value 值
iastore 将栈顶的 value 数值出栈,再将栈顶的 int 类型数值 index 出栈(表示数组的下标),再将栈顶的 int 类型的数组引用类型 arrayref 出栈,根据 arrayref 和 index 定位到具体的数组元素,将该元素的值赋为 value 值
dastore 将栈顶的 value 数值出栈,再将栈顶的 int 类型数值 index 出栈(表示数组的下标),再将栈顶的 double 类型的数组引用类型 arrayref 出栈,根据 arrayref 和 index 定位到具体的数组元素,将该元素的值赋为 value 值
aastore 将栈顶的 value 数值出栈,再将栈顶的 int 类型数值 index 出栈(表示数组的下标),再将栈顶的对象类型的数组引用类型 arrayref 出栈,根据 arrayref 和 index 定位到具体的数组元素,将该元素的值赋为 value 值
arraylength 将栈顶的数组引用值出栈,获取数组的长度,并将其压入栈顶
instanceof 两个,indexbyte1, indexbyte2 根据常量池中第 ((indexbyte1 << 8) &#124; indexbyte2) 个(从 1 开始计数)常量索引指向的类确定具体的类描述信息 class,然后将栈顶元素 objectref 出栈,检查 objectref 是否是指定的类 class,如果是,则将 1 压入栈顶,否则将 0 压入栈顶
checkcast 两个,indexbyte1, indexbyte2 根据常量池中第 ((indexbyte1 << 8) &#124; indexbyte2) 个(从 1 开始计数)常量索引指向的类确定具体的类描述信息 class,然后将栈顶元素 objectref 出栈,检查 objectref 是否可以转换成指定的类 class,如果可以,则将 objectref 压入栈顶,否则抛出 ClassCastException 异常

操作数栈管理指令

助记符 操作数 含义
pop 将操作数栈顶元素出栈
pop2 将操作数栈顶两个元素出栈
dup 将操作数栈顶元素进行复制,然后将复制值压入栈顶
dup2 将操作数栈顶两个元素进行复制,然后按顺序将复制值压入栈顶。具体操作过程如下:将操作数栈顶元素 value2 出栈,再将栈顶元素 value1 出栈,对 value1 进行复制得到 value1_1,对 value2 复制得到 value2_1,然后将 value1 入栈,再将 value2 入栈,再将 value1_1 入栈,再将 value2_1 入栈
dup_x1 复制操作数栈栈顶的值,并插入到栈顶以下 2 个值之后。具体操作过程如下:将栈顶元素 value2 出栈,复制 value2 得到 value2_1,再将栈顶 value1 出栈,然后将 value2_1 入栈,再将 value1 入栈,再将 value2 入栈
dup_x2 复制操作数栈栈顶的值,并插入到栈顶以下 2 个(次栈顶元素是 long 或 double 类型)或 3 个(次栈顶元素不是 long 或 double 类型)值之后。具体操作过程如下(以次栈顶元素不是 long 或 double 类型为例):将栈顶元素 value3 出栈,复制 value3 得到 value3_1,再将栈顶 value2 出栈,再将栈顶 value1 出栈,然后将 value3_1 入栈,再将 value1 入栈,再将 value2 入栈,再将 value3 入栈
dup2_x1 dup_x1 的双倍版本,即复制两个栈顶元素
dup2_x2 dup_x2 的双倍版本,即复制两个栈顶元素
swap 将操作数栈顶的两个元素互换

控制转移指令

助记符 操作数 含义
ifeq 两个,branchbyte1, branchbyte2 将栈顶元素出栈,将这个值与 0 进行比较,如果等于 0,则跳转到 ((branchbyte1 << 8) &#124; branchbyte2) 位置处执行指令,否则继续执行下一条指令
iflt 两个,branchbyte1, branchbyte2 将栈顶元素出栈,将这个值与 0 进行比较,如果小于 0,则跳转到 ((branchbyte1 << 8) &#124; branchbyte2) 位置处执行指令,否则继续执行下一条指令
ifne 两个,branchbyte1, branchbyte2 将栈顶元素出栈,将这个值与 0 进行比较,如果不等于 0,则跳转到 ((branchbyte1 << 8) &#124; branchbyte2) 位置处执行指令,否则继续执行下一条指令
ifge 两个,branchbyte1, branchbyte2 将栈顶元素出栈,将这个值与 0 进行比较,如果大于或等于 0,则跳转到 ((branchbyte1 << 8) &#124; branchbyte2) 位置处执行指令,否则继续执行下一条指令
ifgt 两个,branchbyte1, branchbyte2 将栈顶元素出栈,将这个值与 0 进行比较,如果大于 0,则跳转到 ((branchbyte1 << 8) &#124; branchbyte2) 位置处执行指令,否则继续执行下一条指令
ifle 两个,branchbyte1, branchbyte2 将栈顶元素出栈,将这个值与 0 进行比较,如果小于或等于 0,则跳转到 ((branchbyte1 << 8) &#124; branchbyte2) 位置处执行指令,否则继续执行下一条指令
ifnull 两个,branchbyte1, branchbyte2 将栈顶元素出栈,判断这个值是否是 null,如果是 null, 则跳转到 ((branchbyte1 << 8) &#124; branchbyte2) 位置处执行指令,否则继续执行下一条指令
ifnonnull 两个,branchbyte1, branchbyte2 将栈顶元素出栈,判断这个值是否是 null,如果不是 null, 则跳转到 ((branchbyte1 << 8) &#124; branchbyte2) 位置处执行指令,否则继续执行下一条指令
if_icmpeq 两个,branchbyte1, branchbyte2 将栈顶元素 value2 出栈,再将栈顶元素 value1 出栈,如果 value1 == values, 则跳转到 ((branchbyte1 << 8) &#124; branchbyte2) 位置处执行指令,否则继续执行下一条指令
if_icmpne 两个,branchbyte1, branchbyte2 将栈顶元素 value2 出栈,再将栈顶元素 value1 出栈,如果 value1 != values, 则跳转到 ((branchbyte1 << 8) &#124; branchbyte2) 位置处执行指令,否则继续执行下一条指令
if_icmplt 两个,branchbyte1, branchbyte2 将栈顶元素 value2 出栈,再将栈顶元素 value1 出栈,如果 value1 < values, 则跳转到 ((branchbyte1 << 8) &#124; branchbyte2) 位置处执行指令,否则继续执行下一条指令
if_icmpge 两个,branchbyte1, branchbyte2 将栈顶元素 value2 出栈,再将栈顶元素 value1 出栈,如果 value1 >= values, 则跳转到 ((branchbyte1 << 8) &#124; branchbyte2) 位置处执行指令,否则继续执行下一条指令
if_icmpgt 两个,branchbyte1, branchbyte2 将栈顶元素 value2 出栈,再将栈顶元素 value1 出栈,如果 value1 > values, 则跳转到 ((branchbyte1 << 8) &#124; branchbyte2) 位置处执行指令,否则继续执行下一条指令
if_icmple 两个,branchbyte1, branchbyte2 将栈顶元素 value2 出栈,再将栈顶元素 value1 出栈,如果 value1 <= values, 则跳转到 ((branchbyte1 << 8) &#124; branchbyte2) 位置处执行指令,否则继续执行下一条指令
if_acmpeq 两个,branchbyte1, branchbyte2 将栈顶对象引用类型元素 value2 出栈,再将栈顶对象引用类型元素 value1 出栈,如果 value1 == values, 则跳转到 ((branchbyte1 << 8) &#124; branchbyte2) 位置处执行指令,否则继续执行下一条指令
if_acmpne 两个,branchbyte1, branchbyte2 将栈顶对象引用类型元素 value2 出栈,再将栈顶对象引用类型元素 value1 出栈,如果 value1 != values, 则跳转到 ((branchbyte1 << 8) &#124; branchbyte2) 位置处执行指令,否则继续执行下一条指令
goto 两个,branchbyte1, branchbyte2 无条件跳转到 ((branchbyte1 << 8) &#124; branchbyte2) 位置处执行指令
goto_w 四个,branchbyte1, branchbyte2, branchbyte3, branchbyte4 无条件跳转到 ((branchbyte1 << 24) &#124; (branchbyte2 << 16) &#124; (branchbyte3 << 8) &#124; branchbyte4) 位置处执行指令
jsr 两个,branchbyte1, branchbyte2 无条件跳转到 ((branchbyte1 << 8) &#124; branchbyte2) 位置处执行指令,并将 jsr 的下一条指令地址压如栈顶
jsr_w 四个,branchbyte1, branchbyte2, branchbyte3, branchbyte4 无条件跳转到 ((branchbyte1 << 24) &#124; (branchbyte2 << 16) &#124; (branchbyte3 << 8) &#124; branchbyte4) 位置处执行指令,并将 jsr 的下一条指令地址压如栈顶
ret 一个,index 返回本地变量指定的 index 的指令位置(一般与 jsr 或 jsr_w 联合使用)。index是一个 0 ~ 255 之间的无符号数,它代表一个当前栈帧的局部变量表的索引值,在该索引位置应为一个returnAddress类型的局部变量,指令执行后,将该局部变量的值更新到Java虚拟机的 PC 寄存器中,令程序从修改后的位置继续执行

方法调用指令

助记符 操作数 含义
invokevirtual 两个,indexbyte1, indexbyte2 调用实例方法。具体操作是:取出常量池中第 ((indexbyte1 << 8) &#124; indexbyte2) 个(从 1 开始计数)常量的索引对应的方法描述符,依次弹出操作数栈中的函数个数,再将栈顶的实例引用弹出,根据这些信息调用具体实例的函数
invokespecial 两个,indexbyte1, indexbyte2 调用超类的构造方法,实例初始化方法,私有方法。具体操作是:取出常量池中第 ((indexbyte1 << 8) &#124; indexbyte2) 个(从 1 开始计数)常量的索引对应的方法描述符,依次弹出操作数栈中的函数个数,再将栈顶的实例引用弹出,根据这些信息调用具体函数
invokestatic 两个,indexbyte1, indexbyte2 调用静态方法。具体操作是:取出常量池中第 ((indexbyte1 << 8) &#124; indexbyte2) 个(从 1 开始计数)常量的索引对应的方法描述符,依次弹出操作数栈中的函数个数,根据这些信息调用具体函数
invokeinterface 四个,indexbyte1, indexbyte2, count, 0(第四个操作数恒为 0) 调用接口方法。具体操作是:取出常量池中第 ((indexbyte1 << 8) &#124; indexbyte2) 个(从 1 开始计数)常量的索引对应的方法描述符,根据操作数 count 依次弹出操作数栈中的函数个数,根据这些信息调用具体函数
invodedynamic 四个,indexbyte1, indexbyte2, 0, 0(第三个和第四个操作数恒为 0) 调用动态方法

返回指令

助记符 操作数 含义
ireturn 从当前方法返回 int 类型数值,可用于函数返回类型是 boolean、byte、char、short、int 的函数返回指令中
lreturn 从当前方法返回 long 类型数值
freturn 从当前方法返回 float 类型数值
dreturn 从当前方法返回 double 类型数值
areturn 从当前方法返回对象引用
return 从当前方法返回 void

异常处理指令

助记符 操作数 含义
athrow 将栈顶的异常抛出

同步指令

助记符 操作数 含义
monitorenter 将栈顶的引用对象出栈,获取该对象的锁,用于进入同步块
monitorexit 将栈顶的引用对象出栈,释放该对象的锁,用于退出同步块

参考资料

[1] 周志明. 深入理解Java虚拟机:JVM高级特性与最佳实践[M]. 北京:机械工业出版社,2013
[2] Tim Lindholm, Frank Yellin, Gilad Bracha, Alex Buckley. Java虚拟机规范(Java SE 7版)[M]. 北京:机械工业出版社,2014