3.3 算术运算

  java虚拟机通常在操作数栈上进行算术运算(例外情况是iinc指令,它直接增加一个局部变量的值)。例如下面的align2grain()方法,它的作用是将int值对齐到2的指定次幂:

int align2grain(int i, int grain) {
return ((i + grain-1) & ~(grain-1));
}

  算术运算的操作数是从操作数栈中弹出的,运算结果会压回操作数栈。因此,算术子计算的结果可以作为嵌套计算的操作数。例如。~(grain-1)的计算结果就是被这样使用的:

   iload_2        // Push grain
iconst_1 // Push int constant
isub // Subtract; push result
iconst_m1 // Push int constant -
ixor // Do XOR; push result

  首先使用局部变量2的值和一个int类型的立即数1来计算grain-1的值。这些操作数被操作数栈弹出,然后他们的差值压回操作数栈,这个差值可以立即作为一个操作数被ixor指令使用(回想,~x == -1 ^ x)。同样的,ixor指令的计算结果成为接下来iand指令的一个操作数。

  这个方法的代码如下:

Method int align2grain(int,int)
iload_1
iload_2
iadd
iconst_1
isub
iload_2
iconst_1
isub
iconst_m1
ixor
iand
ireturn

3.4 访问运行时常量池

  许多数值类型常量,以及对象,字段和方法是通过当前类的运行时常量池来访问的。对象的访问在3.8节讨论。数据类型为int,long,float和double以及String类示例的引用通过ldc,ldc_w和ldc2_w来管理。

  ldc和ldc_w指令用于访问运行时常量池中除了double和long类型的值(但是包括String类型实例)。当使用的运行时常量池的项目过多时(多余256个,一个字节能表示的范围),需要使用ldc_w来代替。ldc2_w用于访问所有double和long类型的值,它没有对应的非宽版本,即没有ldc2指令。

  整型常量类型byte,char,short,以及小的int值,可能使用bipush,sipush或者iconst_<i>指令来编译。某些小的浮点型常量也可以使用fconst_<f>和dconst_<d>指令来编译。

  在所有这些情况下,编译都是非常直观的,例如,下面这些常量:

void useManyNumeric() {
int i = 100;
int j = 1000000;
long l1 = 1;
long l2 = 0xffffffff;
double d = 2.2;
...do some calculations...
}

  编译后:

Method void useManyNumeric()
bipush // Push small int constant with bipush
istore_1
ldc # // Push large int constant () with ldc
istore_2
lconst_1 // A tiny long value uses small fast lconst_1
lstore_3
ldc2_w # // Push long 0xffffffff (that is, an int -)
// Any long constant value can be pushed with ldc2_w
lstore
ldc2_w # // Push double constant .
// Uncommon double values are also pushed with ldc2_w
dstore
...do those calculations...

3.5 更多控制结构的示例

  for语句的编译已经在前面的3.2节中展示过了。绝大多数java编程语言的其他控制结构(if-then-else,do,while,break和continue)也同样使用明细的方式来编译。switch语句的编译在单独的章节(3.10)介绍,异常的编译(3.12),finally从句的编译(3.13)。

  作为进一步的示例,while循环以一种明显的方式编译,尽管Java虚拟机提供的特定控制传输指令因数据类型的不同而不同。像往常一样,对int类型的数据有更多的支持,例如:

void whileInt() {
int i = 0;
while (i < 100) {
i++;
}
}

  编译为:

Method void whileInt()
iconst_0
istore_1
goto
iinc
iload_1
bipush
if_icmplt
return

  注意,while语句的条件判断(使用if_icmplt指令实现)位于Java虚拟机代码循环的的底部。(在前面的spin例子中也是如此)。位于循环底部的条件判断强制使用goto指令,以便在循环的第一次迭代之前进行条件判断。如果不满足条件,并且循环体从未进入,那么这个额外的指令就被浪费了。不过while循环通常用于期望循环体会执行的场景中。对于后续的迭代,将判断条件放在循环的底部每次循环时都会节省一条Java虚拟机指令:如果判断条件位于循环的顶部,则循环体将需要一条尾随的goto指令才能回到顶部。

  使用其他数据类型的控制结构都使用相同的方式来编译,但是必须使用对应数据类型的指令。这会导致一些效率不高的代码,因为需要更多的java虚拟机指令,例如:

void whileDouble() {
double i = 0.0;
while (i < 100.1) {
i++;
}
}

  编译成:

Method void whileDouble()
dconst_0
dstore_1
goto
dload_1
dconst_1
dadd
dstore_1
dload_1
ldc2_w # // Push double constant .
dcmpg // To compare and branch we have to use...
iflt // ...two instructions
return

  每一种浮点类型有两个比较指令:float的fcmpl和fcmpg,double的dcmpl和dcmpg。这些指令语义相似,仅仅在对待NaN变量时有所区别。NaN是无序的,所以只要有一个操作数是NaN,浮点指令的比较结果都会失败。无论比较操作是否会因为遇到NaN值而失败,编译器都会根据不用的操作类型来选择不同的比较指令。

java虚拟机规范(se8)——java虚拟机的编译(二)的更多相关文章

  1. java虚拟机规范(se8)——java虚拟机的编译(四)

    3.12 抛出和处理异常 在程序中使用throw关键字来抛出异常.编译结果很简单. void cantBeZero(int i) throws TestExc { if (i == 0) { thro ...

  2. java虚拟机规范(se8)——java虚拟机结构(一)

    本文翻译自:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html 第二章 虚拟机结构 本文档描述了一个抽象的虚拟机规范,并不描述 ...

  3. java虚拟机规范(se8)——java虚拟机结构(二)

    2.5 运行时数据区域 java虚拟机定义了多个用于程序执行期间的运行时数据区域.这些数据区域中一些随着java虚拟机的启动而创建,随着虚拟机的退出而销毁.其他的数据区域时和线程相关的.线程相关数据区 ...

  4. java虚拟机规范(se8)——java虚拟机结构(六)

    2.11 指令集简介 java虚拟机指令由一个字节的操作码,接着时0个或多个操作数组成,操作码描述了执行的操作,操作数提供了操作所需的参数或者数据.许多指令没有操作数只包含一个操作码. 如果忽略异常处 ...

  5. java虚拟机规范(se8)——java虚拟机的编译(一)

    本文翻译自:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html 第三章 java虚拟机的编译 java虚拟机是设计用来支持ja ...

  6. java虚拟机规范(se8)——java虚拟机的编译(三)

    3.6 接受参数 如果n个参数传给一个实例的方法,按照约定,它们被接受并放在这个新方法创建的栈帧中的局部变量表里,在局部变量表中的序号从1到n.这些参数按照它们传递过来的顺序存放.例如: int ad ...

  7. java虚拟机规范(se8)——java虚拟机结构(四)

    2.7 对象的表示 java虚拟机并不要求对象满足任何特定的内部结构. 在Oracle的一些Java虚拟机实现中,对类实例的引用是指向句柄的指针,该句柄本身是一对指针:一个指向包含对象方法的表和指向表 ...

  8. java虚拟机规范(se8)——java虚拟机结构(三)

    2.6. 栈帧 栈帧用于存储数据和部分结果,同样也用于执行动态链接,返回方法的值和分派异常. 当方法被调用的时候会创建一个新的栈帧.当一个方法调用结束时,它对应的栈帧就被销毁了,不管是正常调用结束还是 ...

  9. java虚拟机规范(se8)——java虚拟机结构(五)

    2.10 异常 java虚拟机中的异常用Throwable类或者它的子类的实例来表示.抛出一个异常会导致立即非本地(an inmediate nolocal)的控制转移,从发生异常的地方跳到处理异常的 ...

随机推荐

  1. grep与egrep命令

    greo -E 等同于 egrep grep常用两种方式 1.   grep -c python a.txt 2.   cat a.txt | grep -c python a.txt文件如下 i l ...

  2. python基本数据类型的问题

    关于python中的索引和切片: 在之前看的视屏中是这么描述的:索引值以 0 为开始值,-1 为从末尾的开始位置. 然后今天忽然有了醍醐灌顶的感觉,索引值以 0 为开始值: 就是说从左向右以0开始递增 ...

  3. 61.Merge k Sorted Lists(合并k个排序链表)

    Level:   Hard 题目描述: Merge k sorted linked lists and return it as one sorted list. Analyze and descri ...

  4. Python之字符串转换为日期、结合时区的日期操作

    一.字符串转换为日期 方法一 s = '2019-01-20' print(datetime.strptime(s, '%Y-%m-%d')) # 2019-01-20 00:00:00 方法二 de ...

  5. showmount - 显示关于 NFS 服务器文件系统挂载的信息

    总览 /usr/sbin/showmount [ -adehv ] [ --all ] [ --directories ] [ --exports ] [ --help ] [ --version ] ...

  6. Python中dict的特点

    dict的第一个特点是查找速度快,无论dict有10个元素还是10万个元素,查找速度都一样.而list的查找速度随着元素增加而逐渐下降. 不过dict的查找速度快不是没有代价的,dict的缺点是占用内 ...

  7. QT问题解决

    1.pro文件下各个变量的含义 https://www.zybuluo.com/breakerthb/note/582395 2.如何在pro文件中导入其他的库 https://blog.csdn.n ...

  8. XML 和 HTML 之间的差异

    XML 和 HTML 为不同的目的而设计: XML 被设计用来传输和存储数据,其焦点是数据的内容. HTML 被设计用来显示数据,其焦点是数据的外观. HTML 旨在显示信息,而 XML 旨在存储和传 ...

  9. githup上传项目到仓库

    1.有了自己的账号 2.创建一个新的项目,填写项目名称,描述 填写完成点击create repository 3.复制生成的https链接接下来用到 4.进入到你的项目所在目录右键git bash打开 ...

  10. Qt 浅析Q_PROPERTY宏

    最近在使用QProperAnimation画类,研究这个的时候看到别人写的代码有用到 Q_PROPERTY()这个宏,然后查了下,这个宏只有Qt才有的 并且需要进行编译,继承于QOBJECT Qt 手 ...