Java虚拟机的指令由一个字节长度,代表着某种特定含义的数字,以及其后跟随的零至多个参数构成。Java虚拟机大多数的指令都不包含操作数,只有一个操作码。
由于操作码只有一个字节长度,所以指令集的操作码不能超过256(0-255)。用一个字节来代表操作码,也是为了尽可能获得短小精干的编译代码。
大多数的指令都包含了其操作所对应的数据类型信息。例如iload指令用于从局部变量表中加载int型的数据到操作数栈中,而fload指令加载的是float型数据。
加载和存储指令
加载和存储指令用于将数据在栈帧中的局部变量表和操作数栈之间来回传输。这类指令包括如下内容:
将一个局部变量加载到操作栈:iload,iload_<n>,lload,lload_<n>等。
将一个数值从操作数栈存储到局部变量表:istore,istore_<n>,lstore,lstore<n>等。
将一个常量加载到操作数栈:bipush,sipush等。
扩充局部变量表的访问索引的指令:wide。
例如:iload<n>,代表了iload_0,iload_1,iload_2和iload_3这几条指令(iload_0的语义与操作数为0时的iload指令语义完全一致)。
运算指令
运算或算数指令用于对操作数栈上的值进行某种特定运算,并把结果重新存入到操作栈顶。大体上算数指令可以分为两种:对整数数据进行运算的指令和对浮点型数据进行运算的指令。所有的算数指令如下:
加法指令:iadd,ladd,fadd,dadd。
减法指令:isub,lsub,fsub,dsub。
乘法指令:imul,lmul,fmul,dmul。
除法指令:idiv,ldiv,fdiv,ddiv。
求余指令:irem,lrem,frem,drem。
取反指令:ineg,lneg,fneg,dneg。
位移指令:ishl,lshr,iushr等。
按位或指令:ior,lor。
按位与指令:iand,land。
按位异或指令:ixor,lxor。
局部变量自增指令:iinc。
比较指令:dcmpg,dcmpl等。
类型转换指令
类型转换指令可以将两种不同的数值类型进行相互转换,这些转换操作一半用于实现用户代码中的显式类型转换操作,或者用来处理字节码指令集中数据类型相关指令无法与数据类型一一对应问题。
宽化类型转换(无需显式转换):int转到long,float或者double类型。long转到float或者double。float转到double。
窄化类型转换(需要显式转换):i2b,i2c等,这种转化可能会导致结果产生不同的正负号,不同数量级的情况,导致精度丢失。
对象创建与访问指令
创建类实例的指令:new。
创建数组的指令:newarray,anewarray,multianewarray。
访问类字段(static修饰)和实例字段(非static修饰)的指令:getfiled,putfiled,getstatic,putstatic。
把一个数组元素加载到操作数栈的指令:baload,caload,saload等。
将一个操作数栈的值存储到数组元素的指令:bastore,castore等。
取数组长度的指令:arraylength。
检查类实例类型的指令:instanceof,checkcast。
操作数栈管理指令
将操作数栈的栈顶一个或两个元素出栈:pop,pop2。
复制栈顶一个或两个数值并将复制值或双份的复制值重新压入栈顶:dup,dup2,dup_x1等。
将栈最顶端的两个数值互换:swap。
控制转移指令
控制转移指令可以让Java虚拟机有条件或无条件的,从指定的位置而不是控制转移指令的下一条指令继续执行程序。
条件分支:ifreq,iflt,ifle等。
复合条件分支:tableswitch,lookupswitch。
无条件分支:goto,goto_w等。
方法调用和返回指令
invokevirtual用于调用对象的实例方法,根据对象的实际类型进行分派。
invokeinterface调用接口方法,会在运行时,搜索一个实现了这个接口方法的对象,找出适合的方法进行调用。
invokespecial调用一些需要特殊处理的实例方法。包括实例初始化方法,私有方法和父类方法。
invokestatic调用类方法(static方法)。
invokedynamic用于在运行时,动态解析出调用点限定符所引用的方法,并执行该方法。
方法返回指令根据返回值的类型区分,包括ireturn(返回值是boolean,byte等),lreturn,freturn等。
异常处理指令
Java程序中显式抛出异常的操作,都由athrow实现。而在Java虚拟机中,处理异常(catch语句)不是由字节码指令实现的,而是采用异常表来完成的。
同步指令
Java虚拟机可以支持,方法级的同步,和方法内部一般指令序列的同步。
方法级的同步时隐式的,无需通过字节码指令来控制,实现在方法调用和操作之中。
同步一段指令集序列通常是由synchronized语句块表示的,Java虚拟机的指令集中有monitorenter和monitorexit两条指令来支持synchronized语义,正确实现synchronized关键字需要Javac编译器和Java虚拟机两者共同协作完成。
网友评论