之前的文章就简单的给大家介绍了各种锁的介绍与使用(文末有对应的链接),目前这章为之后讲解锁的操作底层实现先奠定下基础。
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
1: istore_1
2: iconst_1
3: istore_2
4: iload_1
5: iload_2
6: iadd
7: i2b
8: istore_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里面提供的整数运算都基于int和long类型的运算。当小于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)。对于一些简单的程序就可以自己去查看。