java虚拟机规范(se8)——java虚拟机的编译(二)
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虚拟机的编译(二)的更多相关文章
- java虚拟机规范(se8)——java虚拟机的编译(四)
3.12 抛出和处理异常 在程序中使用throw关键字来抛出异常.编译结果很简单. void cantBeZero(int i) throws TestExc { if (i == 0) { thro ...
- java虚拟机规范(se8)——java虚拟机结构(一)
本文翻译自:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html 第二章 虚拟机结构 本文档描述了一个抽象的虚拟机规范,并不描述 ...
- java虚拟机规范(se8)——java虚拟机结构(二)
2.5 运行时数据区域 java虚拟机定义了多个用于程序执行期间的运行时数据区域.这些数据区域中一些随着java虚拟机的启动而创建,随着虚拟机的退出而销毁.其他的数据区域时和线程相关的.线程相关数据区 ...
- java虚拟机规范(se8)——java虚拟机结构(六)
2.11 指令集简介 java虚拟机指令由一个字节的操作码,接着时0个或多个操作数组成,操作码描述了执行的操作,操作数提供了操作所需的参数或者数据.许多指令没有操作数只包含一个操作码. 如果忽略异常处 ...
- java虚拟机规范(se8)——java虚拟机的编译(一)
本文翻译自:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html 第三章 java虚拟机的编译 java虚拟机是设计用来支持ja ...
- java虚拟机规范(se8)——java虚拟机的编译(三)
3.6 接受参数 如果n个参数传给一个实例的方法,按照约定,它们被接受并放在这个新方法创建的栈帧中的局部变量表里,在局部变量表中的序号从1到n.这些参数按照它们传递过来的顺序存放.例如: int ad ...
- java虚拟机规范(se8)——java虚拟机结构(四)
2.7 对象的表示 java虚拟机并不要求对象满足任何特定的内部结构. 在Oracle的一些Java虚拟机实现中,对类实例的引用是指向句柄的指针,该句柄本身是一对指针:一个指向包含对象方法的表和指向表 ...
- java虚拟机规范(se8)——java虚拟机结构(三)
2.6. 栈帧 栈帧用于存储数据和部分结果,同样也用于执行动态链接,返回方法的值和分派异常. 当方法被调用的时候会创建一个新的栈帧.当一个方法调用结束时,它对应的栈帧就被销毁了,不管是正常调用结束还是 ...
- java虚拟机规范(se8)——java虚拟机结构(五)
2.10 异常 java虚拟机中的异常用Throwable类或者它的子类的实例来表示.抛出一个异常会导致立即非本地(an inmediate nolocal)的控制转移,从发生异常的地方跳到处理异常的 ...
随机推荐
- [Java 教程 00] 计算机基础
前言 我想,来到这的朋友肯定是想学习JAVA或者想要进入IT这个行业的.考虑到大家的基础可能不一样,有些人可能还是用着新买的电脑,为了让大家在后续的学习中更加顺畅.在学习一门全新的计算机语言之前,我需 ...
- 【知识强化】第四章 网络层 4.4 IPv4
这节课我们来学习一个非常重要的概念,就是IPv4地址,其实也是我们常用的IP地址. 首先我们来了解一下,为什么会有IP地址的出现以及它是做什么用的.假如说在很多年前我遇到一个女生非常的漂亮,等了十年之 ...
- 【记录】Redis 基础
Redis可以存放五种类型 1:String(字符串) 2:List(列表) 3:Hash(字典) 4:Set(集合) 5:ZSet(有序集合) String (字符串) redis 127.0.0. ...
- Codeforces 1178F DP
题意:有一张白纸条,你需要给这张纸条染色.染色从颜色1开始染色,每次选择纸条的一段染色时,这一段的颜色必须是相同的.现在给你染色后的纸条,问有多少种染色方案? F1: 思路:最开始的想法是以染色顺序为 ...
- 解决"Microsoft Visual C++ 14.0 is required"的问题
1. 在 https://www.lfd.uci.edu/~gohlke/pythonlibs/ 上面找到要安装的组件 2.下载相应的版本到本地 3. pip install **.whl
- WebDriverAgent安装
这次安装WebDriverAgent的过程可谓坎坷呀,最后还是大牛远程解决问题,自己的确差太远,记录一下过程吧 尽量升级Xcode到最新版,保持iPhone的版本大于9.3 终端进入目标文件夹WebD ...
- tensorflow报错
libcublas.so.9.0: cannot open shared object file: No such file or directory 输入命令: sudo ldconfig/usr/ ...
- sqlserver数据导入问题:报错“对COM组件的调用返回了错误HRESULT E_FAIL”
SQL server 2008,导出了两个sql文件. 打开第一个文件,没有问题,建好相应的数据库,运行脚本,即可导入. 第二个文件却遇到问题,始终报错“对COM组件的调用返回了错误HRESULT E ...
- Docker部署Flask应用
创建应用 首先,编写一个简单的Flask应用:docker_test/flask_app.py Docker 安装 请根据自己的操作系统自行安装. Docker简介 Docker 镜像 Docker镜 ...
- 阿里云移动研发平台EMAS,是如何连续5年安全护航双11的?
摘要: 阿里云作为阿里巴巴IT基础设施的基石,每年的双十一都面临前所未有的巨大技术挑战.阿里云的EMAS移动研发平台,连续5年支持双11,不仅保障了手机淘宝.支付宝这些阿里巴巴集团App的使用体验,也 ...