JVM摘要--指令集介绍01

一个JVM的指令包含了1个字节的操作码,该操作码用来表示要操作的动作,然后是0个或多个表示操作码的操作对象:参数或者其他数据。许多指令都没有操作数,而只有一个操作码。
不考虑异常的损耗,JVM解释器的内循环是很有效率的:
do{
atomically calculate pc and fetch opcode at pc;
If (operands) fetch operands;
execute the action for the opcode;
}while(there is more to do)

操作数的数量和大小由操作码决定。比如,如果一个16-bit大小的索引要存放在局域变量中,则要存放在2个字节中,记为byte1和byte2,所以这样存放的变量,其所表达的值为(byte1<<8)|byte2。

1.类型和JVM

大多数JVM中的指令集都要求它们要操作的动作所涉及的类型设定为一种。比如,iLoad指令表示从局部变量中加载的值必须是int类型的。同理,fload也要求加载的值的类型需要时float。这两个指令有相似的应用,但却有不同的操作数。

大多数带有类型要求的指令,指令的类型可以通过字母明显的看出。比如,i表示针对int的操作,l表示long,s表示short,b表示byte,c表示char,f表示float,d表示double,而a表示reference。而有些命令则没有表示类型的字母,比如arrayLength通常都是针对一个数组的操作。而一些类似goto的命令,则不操作带有类型信息的操作数。

从下面的表中,可以看出很多含有类型标注的指令。其中大多数的指中,byte,char和short都是空的。并且其中没有看到boolean的相关内容。怎么回事呢?在编译或运行时,编译器会将byte和short的值利用符号扩展变为int的值,然后再加以使用。而boolean和char的值则利用了0填充的方法将其值转换为int。

同样,从数组中加载boolean,short,byte,和char的值都被JVM在编译或运行期利用符号扩展或0扩展转变成了int类型的值。可以说,boolea,byte,char和short的操作与int的操作用的是一样的指令。
[img]http://glutinit.iteye.com/upload/picture/pic/102264/3a8b39bc-2661-342c-a96a-cea4887964f2.jpg[/img]

2. Load和Store指令

load和store指令是指操作数栈和局部变量区中的值的互相传递,局部变量区(local variables)和操作数栈(operand stack)都属于JVM帧(frame),这些指令包括:

• Load a local variable onto the operand stack: [i]iload, iload_, lload, lload_, fload, fload_, dload, dload_, aload, aload_[/i].
• Store a value from the operand stack into a local variable: [i]istore, istore_,lstore, lstore_, fstore, fstore_, dstore, dstore_, astore, astore_.[/i]
• Load a constant onto the operand stack: [i]bipush, sipush, ldc, ldc_w, ldc2_w,aconst_null, iconst_m1, iconst_, lconst_, fconst_, dconst_.[/i]
• Gain access to more local variables using a wider index, or to a larger immediate operand: [i]wide[/i].

3.算术指令
算术指令,一般都是从操作数栈中获得两个操作数,然后这两个数计算得到结果放回到操作数栈中。根据操作数的类型分,有两大类的算术指令,一是操作整数的指令;一是操作浮点类型的指令。没有直接操作byte,short,char或boolean类型的算术指令,它们都被视为int来进行操作。整数和浮点类型的操作指令的区别还体现在对“溢出”和“被0除”的行为上。算术指令包括:
• Add: iadd, ladd, fadd, dadd.
• Subtract: isub, lsub, fsub, dsub.
• Multiply: imul, lmul, fmul, dmul.
• Divide: idiv, ldiv, fdiv, ddiv.
• Remainder: irem, lrem, frem, drem.
• Negate: ineg, lneg, fneg, dneg.
• Shift: ishl, ishr, iushr, lshl, lshr, lushr.
• Bitwise OR: ior, lor.
• Bitwise AND: iand, land.
• Bitwise exclusive OR: ixor, lxor.
• Local variable increment: iinc.
• Comparison: dcmpg, dcmpl, fcmpg, fcmpl, lcmp.

JVM不会表示出整数类型的“溢出”。只有当除数为0的时候,针对int的操作才会抛出ArithmeticException,会跑出该异常的指令有:idiv, ldiv, irem,lrem; 针对浮点类型的指令遵循IEEE 754的标准。

4.转型指令

转换指令允许JVM的数字类型之间进行转型。

JVM支持一下几种向上转型:
•int to long, float, or double 指令为 i2l, i2f,i2d
•long to float or double 指令为l2f,l2d
•float to double 指令为f2d

其中,i2l, i2d, f2d这三个转型指令不会发生精度丢失的问题。而i2f, l2f, l2d 这三个转型指令可能会有精度丢失的问题,即它们回让最低有效位的一些值丢失.除去有可能的精度丢失,向上转型不会让JVM抛出Runtime Exception;

以及几种向下转型:

•int to byte, short, or char 指令为i2b,i2s,i2c
•long to int 指令为l2i
•float to int or long 指令为f2i,f2l
•double to int, long, or float 指令为d2i,d2l,d2f

显然,向下转型会引起精度丢失,有可能会让数字的正负号发生变化,有可能会让数量发生变化,或者两者都变化。一般来说向下转型要谨慎使用…很多bug都隐藏在向下转型中~

你可能感兴趣的:(JVM,jvm,Instruction)