深入理解java虚拟机(五)--虚拟机字节码执行引擎

一、概述

      执行引擎是Java虚拟机最核心的组成部分之一。在Java虚拟机规范中制定了虚拟机字节码执行引擎的概念模型,这个概念模型成为各种虚拟机执行引擎的统一外观(Facade)。所有的Java虚拟机的执行引擎都是一致的:输入的是字节码文件,处理过程是字节码解析的等效过程,输出是执行结果。

二、运行时栈帧结构

      栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区中的虚拟机栈(Virtual Machine Stack)的栈元素。栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息。每一个方法从调用开始至执行完成的过程,都对应着一个栈帧在虚拟机栈里面从入栈到出栈的过程。

      每一个栈帧都包括了局部变量表、操作数栈、动态连接、方法返回地址和一些额外的附加信息。

      一个线程中的方法调用链可能会很长,很多方法都同时处于执行状态。对于执行引擎来说,在活动线程中,只有位于栈顶的栈帧才是有效的,称为当前栈帧(Current Stack Frame),与这个栈帧相关联的方法称为当前方法(Current Method)。执行引擎运行的所有字节码指令都只针对当前栈帧进行操作。典型的栈帧结构如图8-1。


      1、局部变量表

        局部变量表(Local Variable Table)是一组变量存储空间,用于存放方法参数和方法内部定义的局部变量。在Java程序编译为class文件时,就在方法的Code属性的max_locals数据项中确定该方法所需要分配的局部变量表的最大值。

      局部变量表的容量是以变量槽(Variable Slot,下称Slot)为最小单位,虚拟机规范中并没有明确指明一个Slot应占用的内存空间大小,只是很有向导性的说到每个Slot都应该能存放一个boolean、byte、char、short、int、float、reference或returnAddress类型的数据。

      2、操作数栈

      操作数栈(Operand Stack)也常称为操作栈,它是一个先入后出(Last In First Out,LIFO)栈。操作数栈的最大深度在编译的时候写入到Code属性的max_stacks数据项中。操作数栈的每一个元素可以是任意的Java数据类型,包括long和double。32位数据类型所占的栈容量为1,64为数据类型所占的栈容量为2.在方法执行的任何时候,操作数栈的深度都不会超过在max_stacks数据项中设定的最大值。

      3、动态连接

      每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态链接(Dynamic Linking)。我们知道Class文件的常量池中存有大量的符号引用,字节码中的方法调用指令就以常量池的中指向方法的符号作为参数。这些符号引用一部分会在类加载阶段或者第一次使用的时候转化为直接引用,这种转化称为静态解析。另一部分将在每一次运行期间转化为直接引用,这部分为动态链接。

      4、方法返回地址

      执行方法,退出方式有两种,一种叫正常完成出口(Normal Method Invocation Completion)。另一种叫异常完成出口(Abrupt Method Invocation Completion)。不管什么方法退出,都需要返回到方法被调用的位置,程序才能继续执行,方法返回时可能需要在栈帧中保存一些信息,用来帮助恢复他的上层方法的执行状态。

      5、附加信息

      一般会把动态链接、方法返回地址与其他附加信息全部归为一类,称为栈帧信息。

三、方法调用

      1、解析

        方法在程序真正运行之前就有一个班可确定的调用版本,并且这个方法的调用版本在运行期是不可变的。换句话说,调用目标在程序代码写好、编译器进行编译时就必须确定下来。这类方法的调用称为解析(Resolution)。解析调用一定是个静态过程,在编译期间就完全确定,在类装载的解析阶段就会把涉及的符号引用全部转变为可确定的直接引用。

      2、分派

        分派(Dispatch)调用可能是静态的也可能是动态的。分派可以分为单分派和多分派。两两组合可以有静态单分派、动态单分派、静态多分派和动态多分派四中情况。

        1)、静态分派

        所有依赖静态类型来定位方法执行版本的分派动作称为静态分派。静态分派的典型应用是方法重载。

        2)、动态分派

        运行期根据实际类型确定方法执行版本的分派过程称为动态分派。

        3)、单分派与多分派

        方法的接收者与方法的参数统称为方法的宗量。根据分派基于多少宗量,可以将分派划分为单分派和多分派两种。单分派是根据一个宗量对目标方法进行选择,多分派则根据多余一个宗量对目标方法进行选择。

        4)、虚拟机动态分派的实现

        最常用的是“稳定优化”手段就是为类在方法区中建立一个虚方法表(Virtual Method Table,也称为itable),使用虚方法表索引来代替元数据查找以提高性能。

        图8-3 方法表结构


        虚方法表中存放着各个方法的实际入口地址。如果某个方法在子类中没有被重写,那子类的虚方法表里面的地址入口和父类相同方法的地址入口是一致的,都指向父类的实现入口。如果子类中重写了这个方法,子类虚方法表的地址将会替换为指向子类实现版本的入口地址。

        虚拟机除了使用方法表之外,在条件允许的情况下,还会使用内联缓存(Inline Ccahe)和基于“类型继承关系分析”(Class Hierarchy Analysis,CHA)的技术守护内联(Guarded Inlining)两种非稳定的“激进优化”手段来获得更高的性能。

四、基于栈的字节码解释执行引擎

        1、解释执行

        Java虚拟机遵循的是基于现代经典编译原理的思路,在执行前先对程序源码进行词法分析和语法分析处理,把源码转化为抽象语法树(Abstract Syntax Tree,AST)。Java语言中,Javac编译器完成了程序代码经过词法分析、语法分析到抽象语法树,再遍历语法树生成先性的字节码指令流的过程。因为这一部分动作是在Java虚拟机之外进行的,而解释器在虚拟机内部,所以Java程序的编译就是半独立实现的。图8-4 编译过程

      2、基于栈的指令集与基于寄存器的指令集

      Java编译器输出的指令流,基本上是一种基于栈的指令集架构(Instruction Set Architecture,ISA),指令流中的指令大部分都是零地址指令,它们依赖操作数栈进行工作。另外还有一套常用的基于寄存器的指令集,最典型的就是x86的二地址指令集,这些指令依赖寄存器进行工作。

      基于栈的指令集主要的优点就是可移植,寄存器由硬件直接提供,程序直接依赖这些硬件寄存器则不可避免地要受到硬件的约束。栈架构的指令集还有一些其他的优点,如代码相对更加紧凑、编译器实现更加简单等。

      栈架构指令集的主要缺点是执行速度相对来说会稍微慢一些。

      3、基于栈的解释器执行过程


本文来自于《深入Java虚拟机-JVM高级特性与最佳实践》---周志明。如果侵权,请联系作者删除。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容