JAVA操作码相关指令介绍

723 阅读18分钟

之前的文章就简单的给大家介绍了各种锁的介绍与使用(文末有对应的链接),目前这章为之后讲解锁的操作底层实现先奠定下基础。

JAVA基本类型转换

JAVA中基本类型的转换是不涉及操作数的,转换的值是直接从栈顶端获得,JAVA虚拟机从栈顶弹出一个值,对他进行转换,然后再把转换结果压入栈中。

1.int、long、float、double 类型之间的相互转换

操作码 操作数 备注
i2l 将int类型的值转换为long类类型
i2f 将int类型的值转换为float类类型
i2d 将int类型的值转换为double类类型
l2i 将long类型的值转换为int类类型
l2f 将long类型的值转换为float类类型
l2d 将long类型的值转换为double类类型
f2i 将float类型的值转换为int类类型
f2l 将float类型的值转换为long类类型
f2d 将float类型的值转换为double类类型
d2i 将double类型的值转换为int类类型
d2l 将double类型的值转换为long类类型
d2f 将double类型的值转换为float类类型

2.int数据类型向byte、char、short类型的转换

操作码 操作数 备注
i2b 将int类型的值转换为byte类型值,进行带符号扩展,恢复为int类型压入栈
i2c 将int类型的值转换为char类型值,进行零扩展,恢复成int类型压入栈
i2s 将int类型的值转换为short类型值,进行带符号扩展,恢复成int类型压入栈

不存在比int类型占据更小空间的数据类型转化为int类型操作码。因为上面三种才进行压入栈的时候,就已经转换为int类型,然后再对int类型值进行运算,最后得到int类型值。

Demo:

    public static void main(String[] args) {
		byte a =2;
		byte b =1;
		byte c = (byte) (a+b);
	}
public static void main(java.lang.String[]);
    Code:
       0: iconst_2  //常量2入栈
       1: istore_1  //弹出栈顶元素存入位置1的局部变量
       2: iconst_1  
       3: istore_2  
       4: iload_1   //取出位置1的局部变量值入栈
       5: iload_2 
       6: iadd      //加法运算
       7: i2b       //int类型转化为byte类型值
       8: istore_3  //弹出栈顶元素存入位置3的局部变量
       9: return
}

JAVA常量操作

1.常量值隐含包含在操作码内部

操作码 操作数 备注
iconst_m1 将int类型值-1压入栈
iconst_0 将int类型值0压入栈
iconst_1 将int类型值1压入栈
iconst_2 将int类型值2压入栈
iconst_3 将int类型值3压入栈
fconst_0 将float类型值0压入栈
fconst_1 将float类型值1压入栈
fconst_2 将float类型值2压入栈
lconst_0 将long类型值0压入栈
lconst_1 将long类型值1压入栈
dconst_0 将double类型值0压入栈
dconst_1 将double类型值1压入栈
aconst_null 将空(null)对象压入栈

2.常量值在字节码中跟随操作码之后

操作码 操作数 备注
bipush 一个byte类型的数 将byte类型的数转换为int类型的数,然后压入栈
sipush 一个short类型的数 将short类型的数转换为int类型的数,然后压入栈

3.从常量池中取出常量

操作码 操作数 备注
ldc 无符号8位数indexbyte 从由indexbyte指向的常量池入口中取出一个字长的值,然后将其压入栈
ldc_w 无符号16位数indexshort 从由indexbyte指向的常量池入口中取出两个字长的值,然后将其压入栈

JAVA通用栈以及局部变量的操作

1.通用栈操作

操作码 操作数 备注 Demo
nop 不做任何操作 前:...,word
后:...,word,word
pop 从操作数栈弹出栈顶部一个字 前:...,word
后:...
pop2 从栈顶数弹出最顶端的两个字
swap 交换栈顶部的两个字 前:...,word2,word1
后:...,word1,word2
dup 复制栈顶部的一个字
dup2 复制栈顶部的两个字
dup2 复制栈顶部的两个字
dup_x1 复制栈顶部的一个字,
将复制内容以及原来弹出的两
个字长的内容压入栈
前:...,word2,word1
后:...,word1,word2,word1
dup_x2 复制栈顶部的一个字,
将复制内容以及原来弹出的三
个字长的内容压入栈
前:.,word3,word2,word1
后:.,word1,word3,word2,word1
dup2_x1 复制栈顶部的两个字,
将复制内容以及原来弹出的三
个字长的内容压入栈
dup2_x2 复制栈顶部的两个字,
将复制内容以及原来弹出的四
个字长的内容压入栈

2.局部变量压入栈

1.将一个字长的局部变量压入栈

操作码 操作数 备注
iload vindex 将位置为vindex的int类型的局部变量压入栈
iload_0 将位置为0的int类型的局部变量压入栈
iload_1 将位置为1的int类型的局部变量压入栈
iload_2 将位置为2的int类型的局部变量压入栈
iload_3 将位置为3的int类型的局部变量压入栈
fload vindex 将位置为vindex的float类型的局部变量压入栈
fload_0 将位置为0的float类型的局部变量压入栈
fload_1 将位置为1的float类型的局部变量压入栈
fload_2 将位置为2的float类型的局部变量压入栈
fload_3 将位置为3的float类型的局部变量压入栈

2.将两个字长的局部变量压入栈

操作码 操作数 备注
lload vindex 将位置为vindex和(vindex+1)的long类型的局部变量压入栈
lload_0 将位置为0和1的long类型的局部变量压入栈
lload_1 将位置为1和2的long类型的局部变量压入栈
lload_2 将位置为2和3的long类型的局部变量压入栈
lload_3 将位置为3和4的long类型的局部变量压入栈
dload vindex 将位置为vindex和(vindex+1)的double类型的局部变量压入栈
dload_0 将位置为0和1的double类型的局部变量压入栈
dload_1 将位置为1和2的double类型的局部变量压入栈
dload_2 将位置为2和3的double类型的局部变量压入栈
dload_3 将位置为3和4的double类型的局部变量压入栈

3.将对象引用局部变量压入栈

操作码 操作数 备注
aload vindex 将位置为vindex的对象引用局部变量压入栈
aload_0 将位置为0的对象引用局部变量压入栈
aload_1 将位置为1的对象引用局部变量压入栈
aload_2 将位置为2的对象引用局部变量压入栈
aload_3 将位置为3的对象引用局部变量压入栈

4.弹出栈顶元素,赋值局部变量

移动一个字长的操作:

操作码 操作数 备注
istore vindex 从栈中弹出int类型值,然后存到位置为vindex的局部变量中
istore_0 从栈中弹出int类型值,然后存到位置为0的局部变量中
istore_1 从栈中弹出int类型值,然后存到位置为1的局部变量中
istore_2 从栈中弹出int类型值,然后存到位置为2的局部变量中
istore_3 从栈中弹出int类型值,然后存到位置为3的局部变量中
fstore vindex 从栈中弹出float类型值,然后存到位置为vindex的局部变量中
fstore_0 从栈中弹出float类型值,然后存到位置为0的局部变量中
fstore_1 从栈中弹出float类型值,然后存到位置为1的局部变量中
fstore_2 从栈中弹出float类型值,然后存到位置为2的局部变量中
fstore_3 从栈中弹出float类型值,然后存到位置为3的局部变量中

移动两个字长的操作:

操作码 操作数 备注
lstore vindex 从栈中弹出long类型值,然后存到位置为vindex和(vindex+1)的局部变量中
lstore_0 从栈中弹出long类型值,然后存到位置为0和1的局部变量中
lstore_1 从栈中弹出long类型值,然后存到位置为1和2的局部变量中
lstore_2 从栈中弹出long类型值,然后存到位置为2和3的局部变量中
lstore_3 从栈中弹出long类型值,然后存到位置为3和4的局部变量中
dstore vindex 从栈中弹出double类型值,然后存到位置为vindex和(vindex+1)的局部变量中
dstore_0 从栈中弹出double类型值,然后存到位置为0和1的局部变量中
dstore_1 从栈中弹出double类型值,然后存到位置为1和2的局部变量中
dstore_2 从栈中弹出double类型值,然后存到位置为2和3的局部变量中
dstore_3 从栈中弹出double类型值,然后存到位置为3和4的局部变量中

移动引用的操作: 移动两个字长的操作:

操作码 操作数 备注
astore vindex 从栈中弹出对象引用,然后存到位置为vindex的局部变量中
astore_0 从栈中弹出对象引用,然后存到位置为0的局部变量中
astore_1 从栈中弹出对象引用,然后存到位置为1的局部变量中
astore_2 从栈中弹出对象引用,然后存到位置为2的局部变量中
astore_3 从栈中弹出对象引用,然后存到位置为3的局部变量中

5.wide指令

无符号8位局部变量索引,把方法中局部变量数的限制在256以下。一条单独的wide指令可以将8位的索引再扩展8位。跳转指令并不允许直接跳转到被wide指令修改过的操作码。

操作码 操作数 备注
wide iload,index 从局部变量位置为index的地方取出int类型值,并将其压入栈
wide lload,index 从局部变量位置为index的地方取出long类型值,并将其压入栈
wide fload,index 从局部变量位置为index的地方取出float类型值,并将其压入栈
wide dload,index 从局部变量位置为index的地方取出double类型值,并将其压入栈
wide aload,index 从局部变量位置为index的地方取出对象引用,并将其压入栈
wide istore,index 从栈中弹出int类型值,将其存入位置为index的局部变量中
wide lstore,index 从栈中弹出long类型值,将其存入位置为index的局部变量中
wide lstore,index 从栈中弹出long类型值,将其存入位置为index的局部变量中
wide fstore,index 从栈中弹出float类型值,将其存入位置为index的局部变量中
wide dstore,index 从栈中弹出double类型值,将其存入位置为index的局部变量中
wide astore,index 从栈中弹出对象引用,将其存入位置为index的局部变量中

JAVA数值运算

JAVA里面提供的整数运算都基于intlong类型的运算。当小于int类型的类型参与计算的时候,会将他们转化为int类型。

1.数值之间相加

操作码 操作数 备注
iadd 从栈中弹出两个int类型数,相加之后将所得int类型结果压回栈
ladd 从栈中弹出两个long类型数,相加之后将所得long类型结果压回栈
fadd 从栈中弹出两个float类型数,相加之后将所得float类型结果压回栈
dadd 从栈中弹出两个double类型数,相加之后将所得double类型结果压回栈
iinc vindex,const 把常量与一个位于vindex位置的int类型局部变量相加
wide iinc,indexconst 把常量与一个位于vindex位置的int类型局部变量相加

2.数值之间相减

栈顶端的充当减数,次顶端的充当被减数

操作码 操作数 备注
isub 从栈中弹出两个int类型数,相减所得int类型结果压回栈
lsub 从栈中弹出两个long类型数,相减所得long类型结果压回栈
fsub 从栈中弹出两个float类型数,相减所得float类型结果压回栈
dsub 从栈中弹出两个double类型数,相减所得double类型结果压回栈

3.数值之间乘法

操作码 操作数 备注
imul 从栈中弹出两个int类型数,相乘所得int类型结果压回栈
lmul 从栈中弹出两个long类型数,相乘所得long类型结果压回栈
fmul 从栈中弹出两个float类型数,相乘所得float类型结果压回栈
dmul 从栈中弹出两个double类型数,相乘所得double类型结果压回栈

4.数值之间除法

次顶端的数除以顶端的数。如果整数被0除,会抛出相关异常。

操作码 操作数 备注
idiv 从栈中弹出两个int类型数,相除所得int类型结果压回栈
ldiv 从栈中弹出两个long类型数,相除所得long类型结果压回栈
fdiv 从栈中弹出两个float类型数,相除所得float类型结果压回栈
ddiv 从栈中弹出两个double类型数,相除所得double类型结果压回栈

5.数值之间取余

操作码 操作数 备注
irem 从栈中弹出两个int类型数,取余所得int类型结果压回栈
lrem 从栈中弹出两个long类型数,取余所得long类型结果压回栈
frem 从栈中弹出两个float类型数,取余所得float类型结果压回栈
drem 从栈中弹出两个double类型数,取余所得double类型结果压回栈

6.数值之间取反

操作码 操作数 备注
ineg 从栈中弹出两个int类型数,取反将所得int类型结果压回栈
lneg 从栈中弹出两个long类型数,取反将所得long类型结果压回栈
fneg 从栈中弹出两个float类型数,取反将所得float类型结果压回栈
dneg 从栈中弹出两个double类型数,取反将所得double类型结果压回栈

7.数值之间位移

操作码 操作数 备注
ishl 将int型数值左移位指定位数并将结果压入栈顶
ishr 将int型数值右(符号)移位指定位数并将结果压入栈顶
iushr 将int型数值右(无符号)移位指定位数并将结果压入栈顶
lshl 将long型数值左移位指定位数并将结果压入栈顶
lshr 将long型数值右(符号)移位指定位数并将结果压入栈顶
lshr 将long型数值右(无符号)移位指定位数并将结果压入栈顶

7.数值之间或

操作码 操作数 备注
ior 将栈顶两int型数值作“按位或”并将结果压入栈顶
lor 将栈顶两long型数值作“按位或”并将结果压入栈顶

8.数值之间与

操作码 操作数 备注
iand 将栈顶两int型数值作“按位与”并将结果压入栈顶
land 将栈顶两long型数值作“按位与”并将结果压入栈顶

9.数值之间异或

操作码 操作数 备注
ixor 将栈顶两int型数值作“按位异或”并将结果压入栈顶
lxor 将栈顶两long型数值作“按位异或”并将结果压入栈顶

10.数值之间比较

操作码 操作数 备注
dcmpg 比较栈顶两double型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将1压入栈顶
dcmpl 比较栈顶两double型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将-1压入栈顶
fcmpg 比较栈顶两float型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将1压入栈顶
fcmpl 比较栈顶两float型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将-1压入栈顶
lcmp 比较栈顶两long型数值大小,并将结果(1,0,-1)压入栈顶
ifeq 当栈顶int型数值等于0时跳转
ifne 当栈顶int型数值不等于0时跳转
iflt 当栈顶int型数值小于0时跳转
ifge 当栈顶int型数值大于等于0时跳转
ifgt 当栈顶int型数值大于0时跳转
ifle 当栈顶int型数值小于等于0时跳转

JAVA类相关操作

1.类属性字段操作

操作码 操作数 备注
new 创建类实例
newarray 创建一个指定原始类型(如int,float,char…)的数组,并将其引用值压入栈顶
anewarray 创建一个引用型(如类,接口,数组)的数组,并将其引用值压入栈顶
multianewarray 创建指定类型和指定维度的多维数组(执行该指令时,操作栈中必须包含各维度的长度值),并将其引用值压入栈顶
getfield 获取指定类的实例域,并将其值压入栈顶
putfield 为指定的类的实例域赋值
getstatic 获取指定类的静态域,并将其值压入栈顶
putstatic 为指定的类的静态域赋值

2.数组操作

操作码 操作数 备注
baload 将boolean或byte型数组指定索引的值推送至栈顶
caload 将char型数组指定索引的值推送至栈顶
saload 将short型数组指定索引的值推送至栈顶
iaload 将int型数组指定索引的值推送至栈顶
laload 将long型数组指定索引的值推送至栈顶
faload 将float型数组指定索引的值推送至栈顶
daload 将double型数组指定索引的值推送至栈顶
aaload 将引用型数组指定索引的值推送至栈顶
bastore 将栈顶boolean或byte型数值存入指定数组的指定索引位置
castore 将栈顶char型数值存入指定数组的指定索引位置
sastore 将栈顶short型数值存入指定数组的指定索引位置
iastore 将栈顶int型数值存入指定数组的指定索引位置
fastore 将栈顶float型数值存入指定数组的指定索引位置
dastore 将栈顶double型数值存入指定数组的指定索引位置
aastore 将栈顶引用型数值存入指定数组的指定索引位置

3.方法调用和返回指令

调用之后的结果依旧是在操作栈的

操作码 操作数 备注
invokevirtual 用于调用对象的实例方法,根据对象的实际类型进行分派(虚方法分派),这也是Java语言中最常见的方法分派方式。
invokeinterface 用于调用接口方法,它会在运行时搜索一个实现了这个接口方法的对象,找出适合的方法进行调用。
invokespecial 用于调用一些需要特殊处理的实例方法,包括实例初始化方法(§2.9)、私有方法和父类方法。
invokestatic 调用类方法(static方法)。
ireturn 从当前方法返回int
lreturn 从当前方法返回long
areturn 从当前方法返回对象引用
return 从当前方法返回void

4.其他操作

操作码 操作数 备注
monitorenter 获得对象的锁,用于同步方法或同步块
monitorexit 释放对象的锁,用于同步方法或同步块
athrow 抛出异常信息
jsr 跳转至指定16位offset位置,并将jsr下一条指令地址压入栈顶
jsr_w 跳转至指定32位offset位置,并将jsr_w下一条指令地址压入栈顶
goto_w 无条件跳转(宽索引)

大多数的操作码已经在这两章中介绍了,小伙伴们可以简单的对自己的程序进行编译查看下操作码(javap -help)。对于一些简单的程序就可以自己去查看。