2004-09-30
第六章:jvm虚拟指令集
6.1假定:“必须”的含义
对于jvm指令的一些“必须”的要求,在运行期要求javaclass的结构是满足约束的,对于不满足约束的情况,jvm的行为是未定义的。
6.2保留操作码
在java class文件中使用的指令操作码,有三个操作码是保留的,供java虚拟机内部使用。
254(0xfe)和255(0xff),分别有助记符impdep1和impdep2,目的是在软件或者硬件实现的特定功能提供“后门”或陷阱。
202(0xca),有助记符breakpoint,目的是由调试程序使用来实现断点。
6.3虚拟机错误
当内部错误或者资源限制导致java语言语义不能实现时,jvm抛出一个VirtualMachineError类的子类的实例。jvm规范不规定和语言抛出的时间和地点。
6.4jvm指令集
我不一一例举各种指令的操作码和用法,需要时去查就行了。
第七章 为jvm编译
7.1范例格式
java编译成为class文件之后,以类汇编的jvm操作指令方式存在,jdk自带的javap程序具有这样的功能,将class文件翻译为这样的指令集。
下面是我测试的一个实例:
源文件为test.java
public class test {
public static String a = "";
public int i =0;
public test() {
int i=0;
String a = "xyz";
}
}
编译完成为test.class,用javap生成之后输出结果为:
C:\JBuilderX\jdk1.4\bin>javap -c -classpath "c:/" test
Compiled from "test.java"
public class test extends java.lang.Object{
public static java.lang.String a;
public int i;
static {};
Code:
0: ldc #4; //String
2: putstatic #5; //Field a:Ljava/lang/String;
5: nop
6: return
public test();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."":()V
4: aload_0
5: iconst_0
6: putfield #2; //Field i:I
9: iconst_0
10: istore_1
11: ldc #3; //String xyz
13: astore_2
14: nop
15: return
}
7.2常数、局部变量和控制结构的使用
jvm是面向栈的,大多数操作都是从当前框架的操作数栈中取得操作数。
7。3运算
jvm一般在操作数栈上进行运算,即从栈中取操作数,并将结果存入操作数栈中。
7。4访问常数池
byte,char,short,以及小int值都可以通过立即数的方式编译在method的code中,但是int,long,float,double,以及string实例的引用,就需要对常数池进行访问了。使用ldc,ldc_w,ldc2_w指令管理。
7.5更多控制范例
可供查阅
7。6接收参数
如果向java实例方法传递了n个参数,它们被接收,按约定,框架的编号1到n的局部变量为新的方法调用创建,顺序为接收的顺序。
7。7调用方法
对java实例方法的调用,是在对象的运行期类型上调度的。用invokevirtual实现,将其参数取做对常数池表项的索引,给出对象的类类型的完整限定符名称,再调用方法的名称和方法的描述符。
对java静态(类)方法的调用,无需传递类实例,其余和实例方法相似,用invokespecial方法实现。
invokespecail指令用于调用实例初始化方法。超类方法和调用private方法。
7。8处理类实例
构建一个类实例的过程,在建立对象及其实例域之后,用invokespecial调用相应构造函数对应的方法来初始化对象。
对对象域的访问用getfield和putfield指令完成。
7。9数组
主要讲的是数组操作的相关jvm指令,如:newarray,过一遍,可以查阅。
7。10编译开关
对于java的switch指令,jvm有对应的两种指令:tableswitch,lookupswitch.
tableswitch指定取值范围,而lookupswitch并不指定取值范围,两者如何选择完全由效率选择决定。
7。11对操作数栈的操作
jvm有大量的指令补充,将操作数栈的内容作为无类型字或成对的无类型字操纵。便于jvm虚拟机指令的灵活组成。
如dup,dup_x2等,都是对字或者双字进行操作。
7。12抛出或者处理异常
jvm中try...catch块的处理对于jvm指令处理是透明的,辅助控制是由异常表来完成的,由异常表来决定到哪里去调用处理,哪些部分的异常是受控的。
7。13编译finally
try.....finally语句与try-catch相似,只是其辅助控制是由指令jsr(子例程调用)显式的表示,在jsr调用之前,将下一指令的地址压入栈中。而异常表只控制try块的范围。
7。14同步
jvm用monitorenter和monitorexit指令对同步提供显式支持。而java常用sychronized方法。
sychronized“方法”通常不是用monitorenter和monitorexit指令实现的。往往是由“方法调用指令”检查常数池里的ACC_SYCHRONIZED标志
但monitorenter和monitorexit指令是为了支持sychronized“语句”而存在的。
注意这里的方法和语句的区别。
语句实例如下:test.java
public class test {
public test() {
}
public static void main(String[] args) {
synchronized(new Object()){
int i = 0;
}
}
}
编译完的结果:
C:\JBuilderX\bin>javap -c -classpath "d:/epm40/classes" test
Compiled from "test.java"
public class test extends java.lang.Object{
public test();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."":()V
4: nop
5: return
public static void main(java.lang.String[]);
Code:
0: new #2; //class Object
3: dup
4: invokespecial #1; //Method java/lang/Object."":()V
7: dup
8: astore_1
9: monitorenter
10: iconst_0
11: istore_2
12: nop
13: aload_1
14: monitorexit
15: goto 23
18: astore_3
19: aload_1
20: monitorexit
21: aload_3
22: athrow
23: nop
24: return
Exception table:
from to target type
10 15 18 any
18 21 18 any
}
而synchronized方法编译没有特殊之处,只是在方法名上加了synchronzied字样。