概述
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)| byte2) 运算,得到 short 类型(-32768 ~ 32767),并将其推送到栈顶 |
ldc | 一个,i | 与 ldc_w 类似,将常量池中第 i 个(从 1 开始计数)常量的值取出推送到栈顶,类型可以是 int、float、String。当常量池中常量数量不超过 255 个时,使用此指令,否则使用 ldc_w |
ldc_w | 两个,indexbyte1,indexbyte2 | 与 ldc 类似,将常量池中第 ((indexbyte1 << 8) | indexbyte2) 个(从 1 开始计数)常量的值取出推送到栈顶,类型可以是 int、float、String。当常量池中常量数量超过 255 个时,使用此指令,否则使用 ldc |
ldc2_w | 两个,indexbyte1,indexbyte2 | 将常量池中第 ((indexbyte1 << 8) | 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) | 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) | indexbyte2) 个(从 1 开始计数)常量索引指向的类确定创建数组的类型。然后将栈顶 int 类型的数值出栈,表示创建数组的大小,创建数组完毕后,将数组的引用值压入操作数栈中 |
getfield | 两个,indexbyte1, indexbyte2 | 根据常量池中第 ((indexbyte1 << 8) | indexbyte2) 个(从 1 开始计数)常量索引指向的字段确定具体的字段描述信息,然后将栈顶的对象引用数值出栈,根据对象引用和字段描述信息获取该字段的具体值,然后压入栈顶 |
putfield | 两个,indexbyte1, indexbyte2 | 根据常量池中第 ((indexbyte1 << 8) | indexbyte2) 个(从 1 开始计数)常量索引指向的字段确定具体的字段描述信息,然后将栈顶的数值 value 出栈,再将栈顶的对象引用 objectref 出栈,根据对象引用和字段描述信息获取该字段并将其设置为 value 值 |
getstatic | 两个,indexbyte1, indexbyte2 | 根据常量池中第 ((indexbyte1 << 8) | indexbyte2) 个(从 1 开始计数)常量索引指向的字段获取该字段的具体值,然后压入栈顶 |
putstatic | 两个,indexbyte1, indexbyte2 | 根据常量池中第 ((indexbyte1 << 8) | 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) | indexbyte2) 个(从 1 开始计数)常量索引指向的类确定具体的类描述信息 class,然后将栈顶元素 objectref 出栈,检查 objectref 是否是指定的类 class,如果是,则将 1 压入栈顶,否则将 0 压入栈顶 |
checkcast | 两个,indexbyte1, indexbyte2 | 根据常量池中第 ((indexbyte1 << 8) | 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) | branchbyte2) 位置处执行指令,否则继续执行下一条指令 |
iflt | 两个,branchbyte1, branchbyte2 | 将栈顶元素出栈,将这个值与 0 进行比较,如果小于 0,则跳转到 ((branchbyte1 << 8) | branchbyte2) 位置处执行指令,否则继续执行下一条指令 |
ifne | 两个,branchbyte1, branchbyte2 | 将栈顶元素出栈,将这个值与 0 进行比较,如果不等于 0,则跳转到 ((branchbyte1 << 8) | branchbyte2) 位置处执行指令,否则继续执行下一条指令 |
ifge | 两个,branchbyte1, branchbyte2 | 将栈顶元素出栈,将这个值与 0 进行比较,如果大于或等于 0,则跳转到 ((branchbyte1 << 8) | branchbyte2) 位置处执行指令,否则继续执行下一条指令 |
ifgt | 两个,branchbyte1, branchbyte2 | 将栈顶元素出栈,将这个值与 0 进行比较,如果大于 0,则跳转到 ((branchbyte1 << 8) | branchbyte2) 位置处执行指令,否则继续执行下一条指令 |
ifle | 两个,branchbyte1, branchbyte2 | 将栈顶元素出栈,将这个值与 0 进行比较,如果小于或等于 0,则跳转到 ((branchbyte1 << 8) | branchbyte2) 位置处执行指令,否则继续执行下一条指令 |
ifnull | 两个,branchbyte1, branchbyte2 | 将栈顶元素出栈,判断这个值是否是 null,如果是 null, 则跳转到 ((branchbyte1 << 8) | branchbyte2) 位置处执行指令,否则继续执行下一条指令 |
ifnonnull | 两个,branchbyte1, branchbyte2 | 将栈顶元素出栈,判断这个值是否是 null,如果不是 null, 则跳转到 ((branchbyte1 << 8) | branchbyte2) 位置处执行指令,否则继续执行下一条指令 |
if_icmpeq | 两个,branchbyte1, branchbyte2 | 将栈顶元素 value2 出栈,再将栈顶元素 value1 出栈,如果 value1 == values, 则跳转到 ((branchbyte1 << 8) | branchbyte2) 位置处执行指令,否则继续执行下一条指令 |
if_icmpne | 两个,branchbyte1, branchbyte2 | 将栈顶元素 value2 出栈,再将栈顶元素 value1 出栈,如果 value1 != values, 则跳转到 ((branchbyte1 << 8) | branchbyte2) 位置处执行指令,否则继续执行下一条指令 |
if_icmplt | 两个,branchbyte1, branchbyte2 | 将栈顶元素 value2 出栈,再将栈顶元素 value1 出栈,如果 value1 < values, 则跳转到 ((branchbyte1 << 8) | branchbyte2) 位置处执行指令,否则继续执行下一条指令 |
if_icmpge | 两个,branchbyte1, branchbyte2 | 将栈顶元素 value2 出栈,再将栈顶元素 value1 出栈,如果 value1 >= values, 则跳转到 ((branchbyte1 << 8) | branchbyte2) 位置处执行指令,否则继续执行下一条指令 |
if_icmpgt | 两个,branchbyte1, branchbyte2 | 将栈顶元素 value2 出栈,再将栈顶元素 value1 出栈,如果 value1 > values, 则跳转到 ((branchbyte1 << 8) | branchbyte2) 位置处执行指令,否则继续执行下一条指令 |
if_icmple | 两个,branchbyte1, branchbyte2 | 将栈顶元素 value2 出栈,再将栈顶元素 value1 出栈,如果 value1 <= values, 则跳转到 ((branchbyte1 << 8) | branchbyte2) 位置处执行指令,否则继续执行下一条指令 |
if_acmpeq | 两个,branchbyte1, branchbyte2 | 将栈顶对象引用类型元素 value2 出栈,再将栈顶对象引用类型元素 value1 出栈,如果 value1 == values, 则跳转到 ((branchbyte1 << 8) | branchbyte2) 位置处执行指令,否则继续执行下一条指令 |
if_acmpne | 两个,branchbyte1, branchbyte2 | 将栈顶对象引用类型元素 value2 出栈,再将栈顶对象引用类型元素 value1 出栈,如果 value1 != values, 则跳转到 ((branchbyte1 << 8) | branchbyte2) 位置处执行指令,否则继续执行下一条指令 |
goto | 两个,branchbyte1, branchbyte2 | 无条件跳转到 ((branchbyte1 << 8) | branchbyte2) 位置处执行指令 |
goto_w | 四个,branchbyte1, branchbyte2, branchbyte3, branchbyte4 | 无条件跳转到 ((branchbyte1 << 24) | (branchbyte2 << 16) | (branchbyte3 << 8) | branchbyte4) 位置处执行指令 |
jsr | 两个,branchbyte1, branchbyte2 | 无条件跳转到 ((branchbyte1 << 8) | branchbyte2) 位置处执行指令,并将 jsr 的下一条指令地址压如栈顶 |
jsr_w | 四个,branchbyte1, branchbyte2, branchbyte3, branchbyte4 | 无条件跳转到 ((branchbyte1 << 24) | (branchbyte2 << 16) | (branchbyte3 << 8) | branchbyte4) 位置处执行指令,并将 jsr 的下一条指令地址压如栈顶 |
ret | 一个,index | 返回本地变量指定的 index 的指令位置(一般与 jsr 或 jsr_w 联合使用)。index是一个 0 ~ 255 之间的无符号数,它代表一个当前栈帧的局部变量表的索引值,在该索引位置应为一个returnAddress类型的局部变量,指令执行后,将该局部变量的值更新到Java虚拟机的 PC 寄存器中,令程序从修改后的位置继续执行 |
方法调用指令
助记符 | 操作数 | 含义 |
---|---|---|
invokevirtual | 两个,indexbyte1, indexbyte2 | 调用实例方法。具体操作是:取出常量池中第 ((indexbyte1 << 8) | indexbyte2) 个(从 1 开始计数)常量的索引对应的方法描述符,依次弹出操作数栈中的函数个数,再将栈顶的实例引用弹出,根据这些信息调用具体实例的函数 |
invokespecial | 两个,indexbyte1, indexbyte2 | 调用超类的构造方法,实例初始化方法,私有方法。具体操作是:取出常量池中第 ((indexbyte1 << 8) | indexbyte2) 个(从 1 开始计数)常量的索引对应的方法描述符,依次弹出操作数栈中的函数个数,再将栈顶的实例引用弹出,根据这些信息调用具体函数 |
invokestatic | 两个,indexbyte1, indexbyte2 | 调用静态方法。具体操作是:取出常量池中第 ((indexbyte1 << 8) | indexbyte2) 个(从 1 开始计数)常量的索引对应的方法描述符,依次弹出操作数栈中的函数个数,根据这些信息调用具体函数 |
invokeinterface | 四个,indexbyte1, indexbyte2, count, 0(第四个操作数恒为 0) | 调用接口方法。具体操作是:取出常量池中第 ((indexbyte1 << 8) | 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