JVM虚拟机指令
1概述
a虚拟机的指令由一个字节长度(意味着操作码总数不能超过256条)、代表某种操作含义的数字(操作码,Opcode)以及跟随其后的0至多个代表此操作所需参数的操作数(operand)构成
b因为JVM采用面向操作数栈而不是面向寄存器的架构,所以大多数指令都不包含操作数,只有一个操作码,指令参数都存放在操作数栈中
c操作数的数量以及长度取决于操作码,如果一个操作数的长度超过了一个字节,那么它将会以高位在前(big-endian)顺序存储
d字节码指令流是单字节对齐,只有lookupswitch 和 tableswitch例
e为了尽可能获得短小精悍的编译代码,限制了JVM操作码的长度为一字节,且放弃了编译后代码的参数长度对齐,但这样会损失一些性能
f由于e的原因,JVM处理超过一个字节数据时,不得不在运行时从字节六中重建出具体数据结构,这又会浪费一些性能
g指令集被故意设计成非完全独立,有一些单独的指令可以在必要的时候用来将一些不支持的类型转换为可支持类型(In other words, the instruction set is intentionally not orthogonal. Separate instructions can be used to convert between unsupported and supported data types as necessary)
?
?
2数据类型和JVM(Types and the Java Virtual Machine)
a大部分与数据类型有关的字节码指令,都有特殊字符表明该指令服务哪个数据类型: i for an int operation, l for long, s for short, b for byte, c for char, f for float, d for double, and a for reference.
b JVM会在编译期或运行期将byte和short类型的数据带符号扩展(sign-extend)为相应的int类型数据,将boolean和char类型数据零位扩展(zero-extend)为相应的int数据
c 在处理boolean、byte、short和char类型的数组时,也会转为使用对应的int类型字节码
d JVM指令所支持的数据类型表
3、加载和存储指令
a将一个本地变量加载到操作数栈的指令包括:iload, iload_<n>, lload, lload_<n>, fload, fload_<n>, dload, dload_<n>, aload, aload_<n>
b将一个数值从操作数栈存储到局部变量表的指令包括:istore, istore_<n>, lstore, lstore_<n>, fstore, fstore_<n>, dstore, dstore_<n>, astore, astore_<n>.
c将一个常量加载到操作数栈的指令包括:bipush, sipush, ldc, ldc_w, ldc2_w, aconst_null, iconst_m1, iconst_<i>, lconst_<l>, fconst_<f>, dconst_<d>.
d将一用于扩充局部变量表的访问索引或立即数的指令:wide
Gainaccesstomorelocalvariablesusingawiderindex,ortoalargerimmediate operand: wide.
e补充说明,<n>非负整数,<i>int,<l>long,<f>float,<d>double
?
4算数指令
4.1概述
a JVM没有明确规定整型数据一出情况,但在整数除法指令以及整数求余指令在除数为0时会抛出ArithmeticException
b JVM要求完全支持IEEE754定义的非标准浮点数值和逐级下溢(gradual underflow)
c对long进行比较,采用带符号方式比较。对浮点类型数进行比较,采用IEEE754标准所定义的无信号比较方式(nonsignaling comparisons)
d把浮点型转为整形数时,采用IEEE754标准的向零舍入模式(round towards zero mode)
e JVM在处理浮点数运算时,不会抛出任何JAVA异常。当一个操作向上溢出时,使用有符号的无穷大表示,当向下溢出时,产生非标准值或带符号的0值。如果操作结果没有明确数学定义,则用NaN值表示
f 各种类型的比较最终都会转化为int类型比较。
4.2指令列表
a加法:iadd,ladd,fadd,dadd.
b 减法:isub,lsub,fsub,dsub.
c乘法:imul,lmul,fmul,dmul.
d除法:idiv,ldiv,fdiv,ddiv.
e求余:irem,lrem,frem,drem.
f求负值:ineg,lneg,fneg,dneg.
g移位:ishl,ishr,iushr,lshl,lshr,lushr.
h 按位或: ior, lor.
i按位与: iand, land.
j按位异或: ixor, lxor.
k局部变量自增:iinc
L 比较指令:dcmpg,dcmpl,fcmpg,fcmpl,lcmp
?
5类型转换指令
5.1数值宽化转换( widening numeric conversions,小范围类型到大范围类型的安全转换)
a转换类型:
a.1 int to long, float, or double
a.2 long to float or double
a.3 float to double
b宽化转换指令:i2l, i2f, i2d, l2f, l2d, and f2d
c 从float到double转换,在FP-strict模式下可以保证精确相等,否则不能保证。int到long、int到double能保证精确相等。
d从int或long到float、long到double时,可能会发生精度丢失
e宽化转换虽然可能发生精度丢失,但不会导致JVM抛出异常
f从int到long转换,是一个简单的带符号扩展操作,即把int数值的二进制补码表示扩充至更宽的格式
g从char到一个整形转换时一个零位扩展,即直接给char的二进制形式上填若干0,以填充成更宽的格式
h JVM直接支持转换,不需要显示转换命令
?
5.2数值窄化转换(narrowing numeric conversions)
a类型转换
a.1 int to byte, short, or char
a.2 long to int
a.3 float to int or long
a.4 double to int, long, or float
b窄化转换指令:i2b, i2c, i2s, l2i, f2i, f2l, d2i, d2l, and d2f
c因为窄化转换可能会导致转换结果具备不同的正负号、不同的数量级,因此会导致数值丢失精度
d从int或者long窄化转换为整形T,转过过程仅仅是简单丢低除最低N个二进制以外的内容,这可能导致与原始值有不同符号
e将浮点类型窄化转换为整数T(int或long)时:
e.1如果原始浮点值时NaN,则转换结果都为0
e.2如果转换结果v在T范围内,则转换结果值就为v
e.3如果转换结果v超过T的范围,则转换结果值为T最大值
e.4如果转换结果v低于T的范围,则转换结果值为T最小值
f从double转为float的窄化转换过程与IEEE754中定义一致:
f.1、通过向最近数舍入模式 舍入得到一个可以使用的float类型表示值
f.2、如果转换结果太小,无法用float表示,则返回float类型的正负0;
f.3、如果转换结果太大,则返回float类型的无穷大
f.4 、double的NaN值将转为float的NaN值
g、JVM尽管可能发生上下限溢出或精度丢失等情况,但窄转化永远不会抛出运行时JAVA异常
?
6对象创建与访问指令
a 创建类实例: new.
b 创建数组: newarray, anewarray, multianewarray.
c 访问类字段和类实例字段: getstatic, putstatic, getfield, putfield.
d 把一个数组元素加载到操作数栈:baload,caload,saload,iaload, laload, faload, daload, aaload.
e 讲一个操作数栈的值存储到数组元素中: bastore, castore, sastore, iastore, lastore, fastore, dastore, aastore.
f 取数组长度: arraylength.
g 检查类实例或数组类型: instanceof, checkcast.
?
7操作数栈管理指令
直接控制操作数栈:pop, pop2, dup, dup2, dup_x1, dup2_x1, dup_x2, dup2_x2, swap.
?
8控制转移指令
a 控制转移指令可以让JVM从指定指令而不是控制转移指令的下一条指令继续执行程序(The control transfer instructions conditionally or unconditionally cause the Java Virtual Machine to continue execution with an instruction other than the one following the control transfer instruction)
b指令类型:
b.1条件分支: ifeq, ifne, iflt, ifle, ifgt, ifge, ifnull, ifnonnull, if_icmpeq, if_icmpne, if_icmplt, if_icmple, if_icmpgt if_icmpge, if_acmpeq, if_acmpne.
b.2复合条件分支: tableswitch, lookupswitch.
b.3无条件分支: goto, goto_w, jsr, jsr_w, ret.
c 所有int类型的条件分支转移指令进行的都是有符号的比较操作(All int conditional control transfer instructions perform signed comparisons)
9方法调用和返回指令
9.1方法调用指令
a 指令invokevirtual用于调用对象的实例方法,根据对象的实际类型进行分派(虚方法分派)
b 指令invokeinterface用于调用接口方法,会在运行时搜索指定对象所实现的这个接口方法,并找出合适的方法进行调用
c 指令invokespecial 用于调用一些需要特殊处理的实例方法,包括实例初始化方法、私有方法、父类方法
d 指令invokestatic调用命名类中的类方法
e 指令invokedynamic用于调用以绑定了invokedynamic指令的调用点对象作为目标的方法(invokes the method which is the target of the call site object bound to the invokedynamic instruction)。每条invokedynamic都有独一无二的连接状态(each occurrence of an invokedynamic instruction has a unique linkage state, unlike the other instructions which invoke methods)
9.2方法返回指令
a 指令ireturn 用于返回boolean, byte, char, short, int类型
b 指令lreturn, freturn, dreturn, areturn返回对应类型的值
c 指令return为声明为void的方法、实例初始化方法、类和接口的类初始化方法使用
?
10异常处理指令
a显式异常抛出的操作都由athrow指令实现
b JVM中处理异常,近代JVM都是用异常表实现,很久之前用jsr和ret指令实现
?
11同步指令
a JVM支持方法级的同步和方法内部的一条短指令序列的同步,都是用同步锁实现(monitor,管程,更常见被直接称为锁)支持。
b方法级的同步时隐式的,从方法的常量池中的方法表结构中的ACC_SYNCHRONIZED区分是否同步方法
c同步方法执行期间抛出异常,且方法内部无法处理时,方法所持有的锁在异常抛到同步方法边界之外时自动释放
d指令monitorenter 和monitorexit 用来支持指令序列的同步(synchronized代码块)