一:字节码与数据类型
Java虚拟机的指令由一个字节长度,代表着某种特定操作含义的数字(称为操作码)以及跟其随后的零至多个代表此操作所需参数(称为操作数)而构成。Java虚拟机采用面向操作数栈而不是寄存器的架构,所以大多数指令都不包含操作数。因为字节码指令只有一个字节,所以指令集的操作码总数不可能超过256条。
在Java虚拟机中,大多数的指令都包含了其对操作所对应的数据类型信息。对于大部分与数据类型相关的字节码指令,他们的操作码助记符中都有特殊的字符来表明专门为哪种数据类型服务。i代表int l代表long,s代表short,b代表byte,c代表char,f代表float,a代表reference.
二:加载和存储指令
加载存储指令用于将数据在帧栈中的局部变量表和操作数栈进行来回传输。
①将一个局部变量加载到操作栈:iload,iload<n>,lload,lload<n>,fload,fload<n>,dload,dload<n>,aload,aload<n>
②将一个数值从操作数栈存储到局部变量表中:istore,istore<n>,lstore,lstore<n>,fstore,fstore<n>,dstore,dstore<n>,adore,adore<n>
③将一个常量加载到操作数栈中:bipush,sipush,ldc,ldc_w,ldc2_w,aconst_null,iconst_m1,iconst_<i>,fconst_<i>,dconst_<i>.
④扩充局部变量的访问索引的指令。wide.
指令具体含义如下表所示:
指令码 | 助记符 | 功能描述 | ||||||||||||||
0x00 | nop | 无操作 | ||||||||||||||
| ||||||||||||||||
0x01 | aconst_null |
指令格式: aconst_null
功能描述: null进栈。
注意:JVM并没有为null指派一个具体的值。
| ||||||||||||||
| ||||||||||||||||
0x02 | iconst_m1 | int型常量值-1进栈 | ||||||||||||||
0x03 | iconst_0 | int型常量值0进栈 | ||||||||||||||
0x04 | iconst_1 | int型常量值1进栈 | ||||||||||||||
0x05 | iconst_2 | int型常量值2进栈 | ||||||||||||||
0x06 | iconst_3 | int型常量值3进栈 | ||||||||||||||
0x07 | iconst_4 | int型常量值4进栈 | ||||||||||||||
0x08 | iconst_5 | int型常量值5进栈 | ||||||||||||||
| ||||||||||||||||
0x09 | lconst_0 | long型常量值0进栈 | ||||||||||||||
0x0A | lconst_1 | long型常量值1进栈 | ||||||||||||||
| ||||||||||||||||
0x0B | fconst_0 | float型常量值0进栈 | ||||||||||||||
0x0C | fconst_1 | float型常量值1进栈 | ||||||||||||||
0x0D | fconst_2 | float型常量值2进栈 | ||||||||||||||
| ||||||||||||||||
0x0E | dconst_0 | double型常量值0进栈 | ||||||||||||||
0x0F | dconst_1 | double型常量值1进栈 | ||||||||||||||
| ||||||||||||||||
0x10 | bipush | 将一个byte型常量值推送至栈顶 | ||||||||||||||
0x11 | sipush | 将一个short型常量值推送至栈顶 | ||||||||||||||
| ||||||||||||||||
0x12 | ldc | 将int、float或String型常量值从常量池中推送至栈顶 | ||||||||||||||
0x13 | ldc_w | 将int、float或String型常量值从常量池中推送至栈顶(宽索引) | ||||||||||||||
0x14 | ldc2_w | 将long或double型常量值从常量池中推送至栈顶(宽索引) | ||||||||||||||
| ||||||||||||||||
0x15 | iload | 指定的int型局部变量进栈 | ||||||||||||||
0x16 | lload | 指定的long型局部变量进栈 | ||||||||||||||
0x17 | fload | 指定的float型局部变量进栈 | ||||||||||||||
0x18 | dload | 指定的double型局部变量进栈 | ||||||||||||||
0x19 | aload |
指令格式: aload index
功能描述: 当前frame的局部变量数组中下标为 index的引用型局部变量进栈
index : 无符号一byte整型。和wide指令联用, 可以使index为两byte。
| ||||||||||||||
| ||||||||||||||||
0x1A | iload_0 | 第一个int型局部变量进栈 | ||||||||||||||
0x1B | iload_1 | 第二个int型局部变量进栈 | ||||||||||||||
0x1C | iload_2 | 第三个int型局部变量进栈 | ||||||||||||||
0x1D | iload_3 | 第四个int型局部变量进栈 | ||||||||||||||
| ||||||||||||||||
0x1E | lload_0 | 第一个long型局部变量进栈 | ||||||||||||||
0x1F | lload_1 | 第二个long型局部变量进栈 | ||||||||||||||
0x20 | lload_2 | 第三个long型局部变量进栈 | ||||||||||||||
0x21 | lload_3 | 第四个long型局部变量进栈 | ||||||||||||||
| ||||||||||||||||
0x22 | fload_0 | 第一个float型局部变量进栈 | ||||||||||||||
0x23 | fload_1 | 第二个float型局部变量进栈 | ||||||||||||||
0x24 | fload_2 | 第三个float型局部变量进栈 | ||||||||||||||
0x25 | fload_3 | 第四个float型局部变量进栈 | ||||||||||||||
| ||||||||||||||||
0x26 | dload_0 | 第一个double型局部变量进栈 | ||||||||||||||
0x27 | dload_1 | 第二个double型局部变量进栈 | ||||||||||||||
0x28 | dload_2 | 第三个double型局部变量进栈 | ||||||||||||||
0x29 | dload_3 | 第四个double型局部变量进栈 | ||||||||||||||
| ||||||||||||||||
0x2A | aload_0 |
指令格式:aload_0
该指令的行为类似于aload指令index为0的情况。
| ||||||||||||||
0x2B | aload_1 |
同上
| ||||||||||||||
0x2C | aload_2 |
同上
| ||||||||||||||
0x2D | aload_3 |
同上
| ||||||||||||||
| ||||||||||||||||
0x2E | iaload | 指定的int型数组的指定下标处的值进栈 | ||||||||||||||
0x2F | laload | 指定的long型数组的指定下标处的值进栈 | ||||||||||||||
0x30 | faload | 指定的float型数组的指定下标处的值进栈 | ||||||||||||||
0x31 | daload | 指定的double型数组的指定下标处的值进栈 | ||||||||||||||
0x32 | aaload |
指令格式: aaload
功能描述: 栈顶的数组下标(index)、数组引用 (arrayref)出栈,并根据这两个数值 取出对应的数组元素值(value)进栈。
抛出异常: 如果arrayref的值为null,会抛出 NullPointerException。 如果index造成数组越界,会抛出 ArrayIndexOutOfBoundsException。
index : int类型 arrayref : 数组的引用
| ||||||||||||||
0x33 | baload | 指定的boolean或byte型数组的指定下标处的值进栈 | ||||||||||||||
0x34 | caload | 指定的char型数组的指定下标处的值进栈 | ||||||||||||||
0x35 | saload | 指定的short型数组的指定下标处的值进栈 | ||||||||||||||
| ||||||||||||||||
0x36 | istore | 将栈顶int型数值存入指定的局部变量 | ||||||||||||||
0x37 | lstore | 将栈顶long型数值存入指定的局部变量 | ||||||||||||||
0x38 | fstore | 将栈顶float型数值存入指定的局部变量 | ||||||||||||||
0x39 | dstore | 将栈顶double型数值存入指定的局部变量 | ||||||||||||||
0x3A | astore |
指令格式: astore index
功能描述: 将栈顶数值(objectref)存入当前 frame的局部变量数组中指定下标 (index)处的变量中,栈顶数值出栈。
index : 无符号一byte整数。该指令和wide联 用,index可以为无符号两byte整数。
| ||||||||||||||
| ||||||||||||||||
0x3B | istore_0 | 将栈顶int型数值存入第一个局部变量 | ||||||||||||||
0x3C | istore_1 | 将栈顶int型数值存入第二个局部变量 | ||||||||||||||
0x3D | istore_2 | 将栈顶int型数值存入第三个局部变量 | ||||||||||||||
0x3E | istore_3 | 将栈顶int型数值存入第四个局部变量 | ||||||||||||||
| ||||||||||||||||
0x3F | lstore_0 | 将栈顶long型数值存入第一个局部变量 | ||||||||||||||
0x40 | lstore_1 | 将栈顶long型数值存入第二个局部变量 | ||||||||||||||
0x41 | lstore_2 | 将栈顶long型数值存入第三个局部变量 | ||||||||||||||
0x42 | lstore_3 | 将栈顶long型数值存入第四个局部变量 | ||||||||||||||
| ||||||||||||||||
0x43 | fstore_0 | 将栈顶float型数值存入第一个局部变量 | ||||||||||||||
0x44 | fstore_1 | 将栈顶float型数值存入第二个局部变量 | ||||||||||||||
0x45 | fstore_2 | 将栈顶float型数值存入第三个局部变量 | ||||||||||||||
0x46 | fstore_3 | 将栈顶float型数值存入第四个局部变量 | ||||||||||||||
| ||||||||||||||||
0x47 | dstore_0 | 将栈顶double型数值存入第一个局部变量 | ||||||||||||||
0x48 | dstore_1 | 将栈顶double型数值存入第二个局部变量 | ||||||||||||||
0x49 | dstore_2 | 将栈顶double型数值存入第三个局部变量 | ||||||||||||||
0x4A | dstore_3 | 将栈顶double型数值存入第四个局部变量 | ||||||||||||||
| ||||||||||||||||
0x4B | astore_0 |
指令格式: astore_0
功能描述: 该指令的行为类似于astore指令index 为0的情况。
| ||||||||||||||
0x4C | astore_1 |
同上
| ||||||||||||||
0x4D | astore_2 |
同上
| ||||||||||||||
0x4E | astore_3 |
同上
| ||||||||||||||
| ||||||||||||||||
0x4F | iastore | 将栈顶int型数值存入指定数组的指定下标处 | ||||||||||||||
0x50 | lastore | 将栈顶long型数值存入指定数组的指定下标处 | ||||||||||||||
0x51 | fastore | 将栈顶float型数值存入指定数组的指定下标处 | ||||||||||||||
0x52 | dastore | 将栈顶double型数值存入指定数组的指定下标处 | ||||||||||||||
0x53 | aastore |
指令格式: aastore
功能描述: 根据栈顶的引用型数值(value)、数组下 标(index)、数组引用(arrayref)出 栈,将数值存入对应的数组元素中。
抛出异常: 如果value的类型和arrayref所引用 的数组的元素类型不兼容,会抛出抛出 ArrayStoreException。 如果index造成数组越界,会抛出 ArrayIndexOutOfBoundsException。 如果arrayref值为null,会抛出 NullPointerException。
arrayref : 必须是对数组的引用 index : int类型 value : 引用类型
| ||||||||||||||
0x54 | bastore | 将栈顶boolean或byte型数值存入指定数组的指定下标处 | ||||||||||||||
0x55 | castore | 将栈顶char型数值存入指定数组的指定下标处 | ||||||||||||||
0x56 | sastore | 将栈顶short型数值存入指定数组的指定下标处 |
三:运算指令
运算或算术指令用于对两个操作数栈上的值进行某种特定运算,并把结果重新存入到操作栈顶。注意:由于没有直接支持byte,short,char和boolean类型的算术运算指令,对这种类型直接转换为int进行运算。特别需要注意的是:数据运算可能会导致溢出对象,Java虚拟机在整数没有定义数据异常,所以整数异常不报异常,注意在编程中范围。
加法指令:iadd,ladd,fadd,dadd
减法指令:isub,lsub,fsub,dsub
乘法指令:imul,lmul,fmul,dmul
除法指令:idiv,ldiv,fdiv,ddiv
求于指令:irem,lrem,frem,drem
取反指令:ineg,lneg,fneg,dneg
位移指令:iishl,lishl,fishl,dishl
按位或指令:ior,lor
按位与指令:land,land
按位异或指令:ixor,lxor
局部变量自增指令:inc
比较指令:dcmpg,dcmpl,fcmpg,fcmpl,lcmp
运算指令比较容易理解,这里不详细介绍了。。。。。
四:类型转换指令
类型转换指令可以将两种不同的数值类型进行相互转换,一般用于实现用户代码中的显式类型转换操作。
i2b,i2c,i2s,l2i,f2i,f2l,d2i,d2l和d2f.分为宽化类型转换和窄化类型转换。
五:对象创建与访问指令
虽然类实例和数组都是对象,但Java虚拟机对类实例和数组的创建与操作使用了不同的字节码指令。
创建类实例的指令:new
创建数组的指令:newarray,anewarray,multianewarray
访问类字段:getField,putField(非static字段),getStatic,putStatic(static字段)
把一个数组元素加载到操作数栈的指令:baload,caload,saload,iaload,laload,faload,daload,aaload.
把一个栈值元素存储到数组元素中:bastore,castore,sastore,iastore,lastore,fastore,dastore,aastore.
取数组长度的指令:arrayLength
检查类实例类型的指令:instanceof
new | 创建一个对象,并且其引用进栈 | |||||||||||
0xBC | newarray | 创建一个基本类型数组,并且其引用进栈 | ||||||||||
0xBD | anewarray |
指令格式: anewarray index1 index2
功能描述: 栈顶数值(count)作为数组长度,创建 一个引用 型数组。栈顶数值出栈,数组引 用进栈。
抛出异常: 如果count小于0,会抛出 NegativeArraySizeException
count : int类型。 arrayref : 对所创建的数组的引用。
| ||||||||||
0xBE | arraylength |
指令格式: arraylength
功能描述: 栈顶的数组引用(arrayref)出栈,该 数组的长度进栈。
抛出异常: 如果arrayref的值为null,会抛出 NullPointerException。
arrayref : 数组引用 length : 数组长度 |
五:操作数管理指令
如同操作一个普通数据结构中的堆栈那样,Java虚拟机提供了一些用于直接操作操作数栈的指令
将操作数栈的栈顶一个或两个出栈:pop,pop2
复制栈顶一个或两个数值并将赋制值或双份的复制值重新压入栈顶:dup,dup2,dup_x1,dup2_x1,du[_x2,dup2_x2.
0x59 | dup | 复制栈顶数值,并且复制值进栈 |
0x5A | dup_x1 | 复制栈顶数值,并且复制值进栈2次 |
0x5B | dup_x2 | 复制栈顶数值,并且复制值进栈2次或3次 |
0x5C | dup2 | 复制栈顶一个(long、double型的)或两个(其它类型的)数值,并且复制值进栈 |
0x5D | dup2_x1 |
|
0x5E | dup2_x2 |
|
六:控制转移指令
条件分支:ifeq,iflt,ifle,ifne,ifgt,ifge,ifnull,ifnonnull,if_icmpeq,if_icmpne,ificmplt,ificmpgt,if_icmple,if_icmpge,if_acmpeq,if_acmpne
复合条件分支:tableswitch,lookupswitch
无条件分支:goto,goto_w,jsr,jsr_w,ret
0x99 | ifeq | 当栈顶int型数值等于0时跳转 | ||||||||||
0x9A | ifne | 当栈顶int型数值不等于0时跳转 | ||||||||||
0x9B | iflt | 当栈顶int型数值小于0时跳转 | ||||||||||
0x9C | ifge | 当栈顶int型数值大于等于0时跳转 | ||||||||||
0x9D | ifgt | 当栈顶int型数值大于0时跳转 | ||||||||||
0x9E | ifle | 当栈顶int型数值小于等于0时跳转 | ||||||||||
0x9F | if_icmpeq | 比较栈顶两int型数值大小,当结果等于0时跳转 | ||||||||||
0xA0 | if_icmpne | 比较栈顶两int型数值大小,当结果不等于0时跳转 | ||||||||||
0xA1 | if_icmplt | 比较栈顶两int型数值大小,当结果小于0时跳转 | ||||||||||
0xA2 | if_icmpge | 比较栈顶两int型数值大小,当结果大于等于0时跳转 | ||||||||||
0xA3 | if_icmpgt | 比较栈顶两int型数值大小,当结果大于0时跳转 | ||||||||||
0xA4 | if_icmple | 比较栈顶两int型数值大小,当结果小于等于0时跳转 | ||||||||||
0xA5 | if_acmpeq | 比较栈顶两引用型数值,当结果相等时跳转 | ||||||||||
0xA6 | if_acmpne | 比较栈顶两引用型数值,当结果不相等时跳转 | ||||||||||
| ||||||||||||
0xA7 | goto | 无条件跳转 | ||||||||||
| ||||||||||||
0xA8 | jsr | 跳转至指定16位offset位置,并将jsr下一条指令地址压入栈顶 | ||||||||||
0xA9 | ret | 返回至局部变量指定的index的指令位置(通常与jsr、jsr_w联合使用) | ||||||||||
0xAA | tableswitch | 用于switch条件跳转,case值连续(可变长度指令) | ||||||||||
0xAB | lookupswitch | 用于switch条件跳转,case值不连续(可变长度指令) | ||||||||||
| ||||||||||||
0xAC | ireturn | 当前方法返回int | ||||||||||
0xAD | lreturn | 当前方法返回long | ||||||||||
0xAE | freturn | 当前方法返回float | ||||||||||
0xAF | dreturn | 当前方法返回double | ||||||||||
0xB0 | areturn |
指令格式: areturn
功能描述: 从方法中返回一个对象的引用。
抛出异常: 如果当前方法是 并且当前线程不是改方法的锁的拥有者, 会抛出 IllegalMonitorStateException。
objectref : 被返回的对象引用。
| ||||||||||
0xB1 | return | 当前方法返回void |
七:异常处理指令
Java中的显式抛出异常的操作(throw)都由athrow指令来实现。
整数运算中如果除数为0,则会在idiv或者ldiv中抛出ArithmeticException异常。
八:同步指令
同步一段指令集序列通常是由java语言中的synchronized语句块来表示,JVM指令集中有monitorenter和monitorexit两条指令来支持synchronized关键字的语义。
Java代码段有方法同步和代码同步,其中方法同步可有方法表结构的ACC_SYNCHRONIZED可知用户声明为同步方法。代码块则是由monitorenter和monitorexit支持,通过管程的方式进行同步。