虚拟机字节码执行引擎

来源:互联网 发布:下载班海软件 编辑:程序博客网 时间:2024/05/22 15:36
虚拟机的执行引擎是由自己实现的,因此可以自行制定指令集与执行引擎的结构体系,并且能够执行那些不被硬件直接支持的指令集格式。从外观上看,所有的java虚拟机的执行引擎都是一致的:输入的是字节码文件,处理过程是字节码解析过程,输出的是执行结果

1.运行时栈帧结构
     栈帧是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区中的虚拟机栈的栈元素。栈帧存储了方法的局部变量表、操作数栈、动态链接和方法返回地址等信息。美意个方法从调用开始至执行完成的过程,都对应着一个栈帧在虚拟机里面从入栈到出栈的过程
     在活动线程中,只有位于栈顶的栈帧才是有效的,称为当前栈帧,这个栈帧相关的方法称为当前方法

     1)局部变量表
          局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。局部变量表的容量以变量槽Slot为最小单位,虚拟机规范中并没有指明一个应占用的内存空间大小。对于64位的数据类型,虚拟机以告慰对齐的当时为其分配两个连续的 Slot 空间。局部变量表建立在线程的堆栈上,是线程私有的数据
          顺序为:this  --- 参数 --- 局部变量
          为了尽可能节省栈帧空间,局部变量表中的Slot是可以重用的。但是当某个变量的生命周期结束,但空间内存并未被复用(也就是其值为被覆盖),当回收内存时,可能回收不成功。可以手动将其设置为null值
          类变量有两侧赋初始值的过程,一次在准备阶段,富裕系统初始值,另外一次在初始化阶段,富裕程序员定义的初始值,但一个局部变量定义了但没有赋初始值是不能使用的

     2)操作数栈
          操作数栈也常称为 操作站,它是一个后入先出栈。当一个方法刚刚开始执行的时候,这个方法的操作数栈是空的,在方法的执行过程中,会有各种字节码指令往操作数栈中写入和提取内容,也就是出栈/入栈操作

     3)动态连接
          每个栈帧都包含一个执行运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态链接

     4)方法返回地址
          ①当执行引擎遇到任意一个方法返回的字节码指令
          ②在方法执行过程中遇到了异常 且这个异常没有在方法体内得到处理,这种方式不会给它的上层调用者产生任何返回值
          
          方法退出的过程实际上就等同于把当前栈帧出栈,因此退出时可能执行的操作有:恢复上层方法的局部变量表和操作数栈,把返回值(如果有的话)压入调用者栈帧的操作数栈中,调整PC计数器的值以指向方法调用指令后面的一条指令

   

2.方法调用
     方法调用的任务就是确定调用方法的版本,即确定调用哪一个方法。一切方法调用在Class文件里面都只是符号引用,而不是方法在实际运行时内存布局中的入口地址(即直接引用)
     
     1)解析
          调用目标在程序代码写好、编译器进行编译时就必须确定下来,这类方法的调用称为解析。主要包括静态方法 和 私有方法 -- 类加载阶段进行解析,再加上final 方法

     2)分派
          a.静态分派 -- 方法重载  
               静态分派的典型应用是方法重载,静态分派发生在编译阶段,因此确定静态分派的动作实际上不是由虚拟机来执行的。虚拟机在重载是通过参数的静态类型而不是实际类型作为判定依据的
               当有多个重载方法时,选择执行方法的顺序:  转型 --- 装箱 --- 装箱类实现的借口或父类。可变参数的重载优先级最低

          b.动态分派 -- 多态
               把常量池中的类方法符号引用解析到不同的直接引用上,这个过程就是java语言中方法重写的本质,把这种在运行期根据实际类型确定方法执行版本的分派过程称为动态分派
               invokevirtual指令的多态查找过程:
                    ①找到操作数栈顶的第一个元素所指向的对象的实际类型C
                    ②如果在类型C中找到与常量中描述相符和简单名称都相符的方法,则进行访问权限校验,如果通过则返回这个方法的直接引用,不通过,则抛出异常
                    ③如果没找到。则从下往上一次对C的各个父类进行第二步的搜索和验证
                    ④如果始终没找到,则抛出异常

          c.单分派与多分派
               单分派是根据一个宗量对目标方法进行选择,多分派是根据多于一个宗量对目标方法进行选择。方法的接受者与方法的采纳数统称为方法的宗量
               java语言的静态分派属于多分派(静态类型 和 方法参数),  java语言的动态分派属于单分派类型(只考虑方法接受者的实际类型)

         d.虚拟机动态分派的实现
               在方法区中建立一个虚方法表,使用虚拟方法表索引来代替元数据查找以提高性能
               虚方法表中存放着各个方法的实际入口地址,如果某个方法在子类中没有被重写,那子类的虚方法表里面的地址入口和父类相同方法的地址入口一致,都指向父类的实际入口。方法表一般在类加载的连接阶段进项初始化,准备了类的变量初始值后,虚拟机会把该类的方法表也初始化完毕

     3)动态型语言支持
               



4.基于栈的字节码解释执行引擎
     

     java语言中,javac编译器完成了程序代码经过词法分析、语法分析 到抽象语法树,再便利语法树生成线性的字节码指令流的过程。因为这一部分动作是在java虚拟机之外进行的,而解释器在虚拟机的内部,所以java程序的编译就是半独立的实现

     基于栈的指令集与基于寄存器的指令集
          java编译器输出的指令流,基本上是一种基于栈的指令集架构,指令流中的指令大部分都是零地址指令,它们依赖操作数栈进行工作。与之相对的另外一套常用的指令集架构是基于寄存器的指令集,最典型的就是 x86的二地址指令集
          
          基于栈的指令集:可移植,避免了对硬件的约束(寄存器由硬件直接提供,程序直接依赖这些硬件寄存器则会受到硬件的约束)。--- 栈架构的指令集:获得了尽量红啊的性能,嗲吗相对更加紧凑(字节码每个字节就对应一条指令)、编译实现更加简单(不需要考虑空间分配,因为都在栈上操作);但是执行速度相对来说稍慢一些

0 0