网站建设捌金手指下拉十一,win7做网站,东莞网络推广建站,手机制作网页Java代码运行的过程是Java源码-字节码文件(.class)-运行结果。
Java编译器将Java源文件#xff08;.java#xff09;转换成字节码文件(.class)#xff0c;类加载器将字节码文件加载进内存#xff0c;然后进行字节码校验#xff0c;最后Java解释器翻译成机器码。 … Java代码运行的过程是Java源码-字节码文件(.class)-运行结果。
Java编译器将Java源文件.java转换成字节码文件(.class)类加载器将字节码文件加载进内存然后进行字节码校验最后Java解释器翻译成机器码。 图 Java编译过程
1 字节码简介
Java虚拟机的指令由一个字节长度的、代表着某种特定操作含义的数字称为操作码Opcode以及跟随其后的0至多个代表此操作所需的参数称为操作数Operand构成。
1.1 一个字节长度的操作码
缺点
1一个字节0255这意味着指令集的操作码总数不能超过256条。
2由于Class文件格式放弃了编译后代码的操作数长度对齐这意味着虚拟机在处理那些超过一个字节的数据时不得不在运行时从字节中重建出具体的数据结构。 图 操作数对齐与不对齐情况下存储char的情况
当不对齐时存储一个char类型占2个字节,需要用两个无符号字节存储起来则值为 (byte1 8) | byte2。 当对齐时直接表示为byte0。所以不对齐时这种操作某种程度上会导致解释器执行字节码时损失些性能。
优点
1放弃操作数长度对齐就意味着可以省略掉大量的填充和间隔符号。
2用一个字节来代表操作码也是为了尽可能获得短小精干的编译代码。
1.2 执行模型
如果不考虑异常处理的话那Java虚拟机的解释器可以使用以下这段伪代码作为最基本的执行模型来理解
do {PC寄存器的值1;根据P C寄存器指示的位置从字节码流中取出操作码;if (字节码存在操作数) 从字节流中取出操作数;执行操作码所定义的操作;} while (字节码流长度 0 );
1.3 异常表
在Java虚拟机中处理异常不是由字节码指令来实现而是采用异常表来完成的。
异常表(exception_table)是存储于属性表(attribute_info)中的Code属性表中的一个结构这个结构是可选的。 类型 名称 数量 说明 u2 start_pc 1 try的范围的起始位置。 u2 end_pc 1 try的范围的终止位置。 u2 handler_pc 1 出现类型为catch_type或者其子类的异常则跳转到第handler_pc行执行。 u2 catch_type 1 异常类型是指向一个CONSTANT_Class_info型常量的索引。如果为0表示任意异常情况都需要转到handler_pc处进行处理。
表 异常表的结构
当程序触发异常时JVM会从上至下遍历异常表中的所有条目。当触发异常的字节码索引值在某个异常表条目的监控范围内JVM会判断所抛出的异常和该条目的catch_type是否匹配如果匹配JVM会将控制流转移至该条目的handle_pc所指向的位置。如果遍历完所有异常表条目JVM仍未匹配到异常处理器那么它会弹出当前方法对应的Java栈帧并在调用者中重复上诉操作。
2 字节码指令
2.1 字节码与数据类型
在Java虚拟机的指令集中大多数指令都包含其操作对应的数据类型信息。a代表reference。
iload指令用于从局部变量表中加载int型的数据到操作数栈中。而fload指令加载到则是float类型的数据。
大部份指令都没支持byte、char和short甚至没有任何指令支持boolean类型。编译器会在编译器或运行期将byte和short类型的数据带符号扩展为相应的int类型数据将boolean和char类型数据零位扩展为相应的int类型数据。
2.2 加载和存储指令
将一个局部变量加载到操作栈iload、iload_n、lload;
将一个数值从操作数栈存储到局部变量表istore、istore_n、astore;
将一个常量加载到操作数栈bipush、sipush、ldc、ldc_w、iconst_ml;
扩充局部变量表的访问索引的指令: wide
以尖括号结尾的例如iload_n这些指令助记符实际上代表了一组指令例如iload_n,它代表了iload_0、iload_1。
2.3 运算指令 加法指令 iadd、ladd、fadd 减法指令 isub、lsub 乘法指令 imul、lmul 除法指令 idiv、ldiv、fdiv 求余指令 irem、lrem、frem、drem 取反指令 ineg、lneg、fneg 位移指令 ishl、ishr 按位或指令 ior、lor 按位与指令 iand、land 按位异或指令 ixor、lxor 局部变量自增指令 iinc 比较指令 dcmpg、dcmpl、fcmpg
表 算术指令
不存在直接支持byte、short、char和boolean类型的算术指令。对于这些数据类型应使用操作int类型的指令代替。
2.4 类型转换指令
类型转换指令可以将两种不同的数值类型相互转换这种转换一般用于实现用户代码中的显式类型转换操作或者用来处理字节码指令集中数据类型相关指令无法与数据类型一一对应的问题。
这些转换指令包括:i2b、i2c、i2s、l2i、f2i、f2l、d2i、d2l和d2f。
2.5 对象创建和访问指令 创建类实例 new 创建数组 newarray、anewarray、multianewarrray 访问类字段和实例字段 getfield、putfield、getstatic、putstatic 把一个数组元素加载到操作数栈 baload、caload、iaload… 将一个操作数栈的值存储到数组元素 bastore、iastore 取数组长度 arraylength 检查类实例类型 instanceof、checkcast
表 对象创建与访问指令
2.6操作数栈管理指令
将操作数栈到栈顶一个或两个元素出栈pop、pop2;
复制栈顶一个或两个数值并将复制值或双份的复制值重新压入栈顶dup、dup2、dup_x1、dup2_x1、dup_x2、dup2_x2;
将栈最顶端的两个数值互换swap
2.7 控制转移指令
控制转移指令可以让Java虚拟机有条件或无条件地从指定位置指令而不是控制转移指令的下一条指令继续执行程序从概念模型上理解可以认为控制指令就是在有条件或无条件地修改PC寄存器地值控制转移指令包括:
条件分支ifeq、iflt…
复合条件分支tableswitch、lookupswitch
无条件分支goto、goto_w、jsr、jsr_w、ret
各种类型的比较最终都会转化为int类型的比较操作。
2.8 方法调用和返回指令
invokevirtual指令用于调用对象的实例方法根据对象的实际类型进行分派虚方法分派。
invokeinterface指令用于调用接口方法它会在运行时搜索一个实现了这个接口方法的对象找出合适的方法进行调用。
invokespecial指令用于调用一些需要特殊处理的实例方法包括实例初始化方法、私有方法和父类方法。
invokestatic指令用于调用类静态方法。
invokedynamic指令用于在运行时动态解析出调用点限定符所引用的方法。并执行该方法。
方法调用指令与数据类型无关而方法返回指令是根据返回值的类型区分的。包括ireturn、lreturn、freturn、dreturn和areturn。return指令为void的方法。
2.9 异常处理指令
在Java程序中显式抛出异常的操作(throw语句)都由athrow指令来实现。处理异常不是由字节码指令来实现的而是采用异常表。
2.10 同步指令
Java虚拟机可以支持方法级的同步和方法内部一段指令序列的同步这两种同步结构都是使用管程(Monitor也称为锁)来实现的。
方法级的同步是隐式的无须通过字节码指令来控制。
同步一段指令集序列通常是由Java语言中的synchronized语句来表示的Java虚拟机的指令集中有monitorenter和monitorexit两条指令来支持synchronized关键字的语义。