深入理解Java虚拟机—虚拟机字节码执行引擎

虚拟机字节码执行引擎

概述

“虚拟机”是一个相对于“物理机”的概念,这两种及其都有代码执行能力。
其区别主要在于:

  • 物理机的执行引擎是直接建立在处理器、硬件、指令集和操作系统层面上的。
  • 虚拟机的执行引擎则是由自己实现的,因此可以自行制定指令集和执行引擎的体系结构,并且能够执行那些不被硬件直接支持的指令集格式。

所有Java虚拟机的执行引擎都是一致的:输入的是字节码文件,处理过程是字节码解析的等效过程,输出的是执行结果。

运行时栈帧结构

栈帧概述

  • 栈帧
    栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区中的虚拟机栈的栈元素。

  • 栈帧的内容
    栈帧存储了方法的局部变量表、操作数栈、动态链接和方法返回地址等信息。


    8_1.png
  • 栈帧的运行
    对于执行的引擎中,在活动线程中,只有位于栈顶的栈帧才是有效的,称为当前栈帧,与这个栈帧相关联的方法称为当前方法。执行引擎的所有字节码指令都只对当前栈帧进行操作。

局部变量表

  • 概述
    局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。

  • 局部变量表的数据结构

    • 局部变量表以变量槽(Variable Slot)为最小单位,虚拟机中并未指明一个Slot的内存大小,但是有导向性的说明可以使用32位或者更小的物理内存。
    • 如果是64位数据类型的变量,会同时使用两块连续的32位的Slot。对于这种情况,不允许单独访问其中的一个,如果有这种操作,则会在类加载的校验阶段抛出异常。
  • 局部变量表的内容

    • 第0位索引的Slot默认是用于传递方法所属对象实例的引用,可以通过this来访问到这个参数。
    • 从1位索引开始分配形参
    • 然后再分配局部变量
  • 局部变量表中Slot的重用

    • 方法体中定义的变量其作用域并不会覆盖整个方法体(比如循环体中定义的变量),如果当前字节码PC计数器的值已经超出某个变量的作用域(比如退出循环),那么这个变量对应的Slot就可以交给其他变量使用。
    • Slot的重用是为了节约栈帧空间,但是会伴随着一些副作用(比如影响到GC)
  • 方法中的局部变量

    • 方法中的局部变量并没有类变量在类加载时候的“准备阶段”,也就是没有赋初始值的阶段。
    • 如果一个局部变量定义了但没有赋初值是不能使用的,但好在编译器能在编译阶段察觉到并给出错误。

操作数栈

  • 概述
    操作数栈(Operand Stack)也称为操作数栈,它是一个后入先出栈。
  • 作用
    当一个方法刚开始执行的时候,这个方法的操作数栈是空的,在方法的执行过程中,会有各种字节码往操作数栈中写入和提取内容,也就是入栈和出栈操作。
  • 栈帧的优化
    在概念模型中,两个栈帧作为虚拟机栈的元素,是完全独立的。但在大多数虚拟机的实现里都会做一些优化,令两个栈帧出现一部分重叠。


    8_2.png

动态连接

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

方法返回地址

  • 概述
    在方法退出(包括异常退出)之后,需要返回到方法被调用的位置,程序才能继续执行,方法返回是可能需要在栈帧中保存一些信息,用来帮助恢复它的上层方法的执行状态。
  • 方法退出的方式
    • 正常的方式(正常完成出口)
      当执行引擎遇到任意一个方法返回的字节码指令时。这时候可能有返回值传递给上层的调用者。
    • 异常的方式(异常完成出口)
      当方法执行的过程中遇到异常。没有任何返回值返回给上层调用者。

附加信息

虚拟机规范允许具体的虚拟机实现增加一些规范里没有描述的信息到栈帧里面,这部番完全取决于具体的虚拟机实现。

方法调用

解析

  • 概括
    Java中所有方法的目标方法在Class文件里面都是一个常量池中的符号引用,在类加载的解析阶段,会将其中一部分符号引用转化为直接引用,这种解析能成立的前提是:方法在程序真正运行之前就有一个可确定的调用版本,并且这个方法的调用版本在运行期是不可改变的。这类方法的调用称为解析

  • 方法调用的指令
    在Java语言中符合“编译器可知,运行期不可变”这个要求的方法,主要包括静态方法私有方法*两大类,前者与类型直接关联,后者在外部不可访问。
    与之相对应的是Java在虚拟机里提供了五条方法调用指令:

    • invokestatic:调用静态方法
    • invokespecial:调用实例构造器<init>方法、私有方法和父类方法。
    • invokevirtual:调用所有虚方法
    • invokeinterface:调用接口方法
    • invokedynamic:现在运行时动态解析出调用点限定符所引用的方法,然后再执行该方法
  • 非虚方法
    只要能被invokestaticinvokespecial指令调用的方法和fina方法(final方法被invokevirtual调用),都可以在解析阶段中确定唯一的调用版本,它们在类加载的时候就会把符号引用解析为该方法的直接引用。

分派

  • 静态分派
    所有通过静态类型来定位方法执行版本的分配动作称为静态分派。静态分派的经典应用是重载
  • 动态分派
    在运行期根据实际类型确定方法执行版本的分配称为动态分配。动态分派的经典应用是重写
  • 单分派与多分派
    • 概述
      方法的接受者(调用方法的对象)与方法的参数统称为方法的宗量。根据分派基于多少宗量可以将分派划分为单分派和多分派。
    • Java语言静态分派属于多分派
    • Java语言动态分派属于单分派
  • 虚拟机动态分派的实现
    • 背景
      由于动态分配是非常频繁的操作,因此虚拟机在实际实现中基于性能的考虑,大部分实现都不会真正的进行如此频繁的操作。
    • “稳定优化”手段
      为类在方法区建立一个虚方法表,与此对应,在invokevirtual执行的时候也会用到接口方法表。使用虚方法表索引来代替元数据查找以提高性能。
      虚方法表一般在类加载的连接阶段进行初始化,装备了类的变量初始值后,虚拟机会把该类的方法表也初始化完毕。
    • “激进优化”手段
      使用内联缓存和基于“类型继承关系分析”技术的守护内联两种非稳定的“激进优化”的手段来获得更高的性能。

动态类型语言支持

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

  • 解释执行


    8_4.png
    • 下面的那条分支是传统编译原理中程序代码到目标机器代码的生成过程

    • 中间那条就是解释执行的过程

    • Java语言中,Javac编译器完成了程序代码经过词法分析、语法分析到抽象语法树,再遍历语法树生成线性的字节码指令流的过程,这一部分是在Java虚拟机之外的过程。而解释器在虚拟机的内部,所以Java的编译就是半独立的实现。

  • 基于栈的指令集与基于寄存器的指令集
    基于栈的指令集的优点:

    • 可移植
    • 代码相对紧凑
    • 编译器实现更加简单

    基于栈的指令集的缺点:

    • 速度相对来说要慢些

总结

  • 主要介绍了虚拟机进行方法调用和方法执行时的数据结构(栈帧),主要包括局部变量表、操作数栈、动态连接、方法返回地址和附加信息。
  • 紧接着介绍方法的调用时虚拟机内部的实现方法,主要有解析和分派,分别适应于不同的类型的方法。
  • 之后介绍了关于Java动态语言的支持,包括虚拟机指令invokedynamicMethodHandle.
  • 最后介绍了基于栈的字节码执行引擎,并分析了相对于基于寄存器的字节码寄存器的优缺点,然后再演示了基于栈的字节码解释器的执行过程。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335