为JAVA虚拟机编译(第1部分)
本章只涉及到从使用Java语言编写的源代码编译为Java虚拟机指令集的编译器。
3.1 示例的格式说明
本章节的示例主要包括有源文件和Java虚拟机代码注解列表(Annotated Listings),其中,Java虚拟机的代码注解列表是由Oracle的1.0.2版本的JDK的javac编译器生成。
Java虚拟机代码将使用Oracle的javap工具所生成的非正式的“虚拟机汇编语言“格式来描述。
<index> <opcode> [<operand1> [<operand2>...]] [<comment>]
8 bipush 100 // Push int constant 100
3.2 常量、局部变量的使用和控制结构
spin()是一个很简单的方法,它进行了100次空循环
void spin() {
int i;
for (i = 0; i < 100; i++) {
; // Loop body is empty
}
}
编译后代码如下:
Method void spin()
0 iconst_0 // Push int constant 0
1 istore_1 // Store into local variable 1 (i=0)
2 goto 8 // First time through don’t increment
5 iinc 1 1 // Increment local variable 1 by 1 (i++)
8 iload_1 // Push local variable 1 (i)
9 bipush 100 // Push int constant 100
11 if_icmplt 5 // Compare and loop if less than (i < 100)
14 return // Return void when done
Java虚拟机是基于栈架构设计的,它的大多数操作是从当前栈帧的操作数栈取出1个或多个操作数,或将结果压入操作数栈中。每调用一个方法,都会创建一个新的栈帧,并创建对应方法所需的操作数栈和局部变量表。每条线程在运行时的任意时刻,都会包含若干个由不同方法嵌套调用而产生的栈帧,当然也包括了若干个栈帧内部的操作数栈,但是只有当前栈帧中的操作数栈才是活动的。
istore_1指令作用是从操作数栈中弹出一个int型的值,并保存在第一个局部变量中。
iload_1指令作用是将第一个局部变量的值压入操作数栈。
iinc指令的作用是对局部变量加上一个长度为1字节有符号的递增量。
if_icmplt指令将100从操作数栈中弹出值并与i进行比较,如果满足条件(即i的值小于 100),将转移到索引为5的指令继续执行
double类型的值占用两个局部变量的空间。譬如下面例子展示了double类型值的访问:
double doubleLocals(double d1, double d2) {
return d1 + d2;
}
编译后代码如下:
Method double doubleLocals(double,double)
0 dload_1 // First argument in local variables 1 and 2
1 dload_3 // Second argument in local variables 3 and 4
2 dadd
3 dreturn
注意:局部变量表中使用了一对局部变量来存储doubleLocals()方法中的double值,这对局部变量不能被分开来进行单个操作。
spin()方法的for循环语句中,对于int型值的判断可以统一用if_icmplt指令实现;但是,在Java虚拟机指令集合中,对于double类型的值的没有这样的指令。如果把spin方法变量改为double,必须在dcmpg指令后面再联合iflt指令来实现。
如果把spin方法变量改为double型,编译后代码如下:
Method void spin()
0 dconst_0 // Push double constant 0.0
1 dstore_1 // Store into local variables 1 and 2
2 goto 9 // First time through don’t increment
5 dload_1 // Push local variables 1 and 2
6 dconst_1 // Push double constant 1.0
7 dadd // Add; there is no dinc instruction
8 dstore_1 // Store result in local variables 1 and 2
9 dload_1 // Push local variables 1 and 2
10 ldc2_w #4 // Push double constant 100.0
13 dcmpg // There is no if_dcmplt instruction
14 iflt 5 // Compare and loop if less than (i < 100.0)
17 return // Return void when done
如果把变量改为short型,编译后的代码:
Method void spin()
0 iconst_0
1 istore_1
2 goto 10
5 iload_1 // The short is treated as though an int
6 iconst_1
7 iadd
8 i2s // Truncate int to short
9 istore_1
10 iload_1
11 bipush 100
13 if_icmplt 5
16 return
Java虚拟机支持下,对int类型的数据的大部分操作可以直接进行。这在一定程度上是考虑到了Java虚拟机操作数栈和局部变量表的实现效率。当然也有考虑到了大多数程序都会对int型数据进行频繁操作的原因。
在Java虚拟机中,缺乏对byte、char和short类型数据直接操作的支持所带来的问题并不大,因为这些类型的值都在编译过程中就自动被转换为int类型
Java虚拟机对于long和浮点类型(float和double)提供了中等程度的支持,比起int类型数据所支持的操作,它们仅缺少了条件转移指令部分,其他操作都与int类型具有相同程度的支持。
说明
《Java虚拟机规范(Java SE 7版)》作者:Tim Lindholm、Frank Yellin、Gilad Bracha、Alex Buckley 摘要:第三章 为JAVA虚拟机编译(第1部分)
你也可以访问:http://txidol.github.io 获取更多的信息