定义

Java虚拟机字节码执行引擎是jvm最核心的组成部分之一,它做的事情很简单:输入的是字节码文件,处理过程是字节码解析的等效过程,输出的是执行结果。在不同的虚拟机实现里,执行引擎在执行java代码的时候可能会有解释执行和编译执行两种选择,也可能两者兼备。

运行时栈帧结构

java字节码执行引擎在调用和执行方法的时候使用了一种叫做栈帧的数据结构。

在jvm的内存结构里,存在这一块称为虚拟机栈的内存区域,虚拟机栈中的元素就是栈帧。每个方法的调用至结束对应着一个栈帧在虚拟机栈的入栈和出栈。

栈帧数据结构示意图:

如图所示,因为虚拟机栈是线程私有的,所以每个线程都有自己的虚拟机栈;而每个线程的虚拟机栈中都有多个栈帧对应一个方法调用链中的多个方法,栈顶的栈帧是当前正在执行的方法;每个栈帧主要包含4个部分:

局部变量表

操作数栈

动态连接

方法返回地址

在编译代码的阶段,栈帧中需要多大的局部变量表,多深的操作数栈都是已经完全确定的,而且会写入到方法表的Code属性中。

接下来一次介绍栈帧中的4个部分:

局部变量表

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

局部变量表的容量以变量槽Slot为最小单位,规定一个Slot可以存放一个32位以内的数据类型,java中占32位以内的数据类型有8种基本数据除了long和double以外的6种,还有reference和returnAddress,共8种类型。而对于64位的数据类型,即long和double,则会以高位对齐的方式为其分配两个连续的Slot空间。

在方法执行时,方法的参数列表是通过局部变量表传递的,如果执行的是实例方法(非static方法),则局部变量表中的第0位索引的Slot默认保存方法所属对象的引用,以支持“this”关键字来访问对象数据。其余参数则按参数列表顺序,占用从1开始的局部变量Slot,参数表分配完成后,在根据方法体内局部变量定义的顺序和作用域为局部变量分配其余Slot。之所以这里要强调作用域是因为为了节省栈帧空间,局部变量表中的Slot是可以重用的,当一个Slot中保存的局部变量超出其作用域后,这个Slot可以被复用来存储新的变量。

操作数栈

操作数栈是用来执行字节码命令的,比如iadd命令在运行的时候,就是把操作数栈中最接近栈顶的2个元素出栈相加,然后将结果入栈。

操作数栈的每一个元素可以是任意的java数据类型,32位数据所占栈容量为1,64位数据所占栈容量为2。

java虚拟机的解释执行引擎是“基于栈的执行引擎”,这里的栈就是指操作数栈。

动态连接

一个指向运行时常量池的引用,用来支持当前方法的代码实现动态链接。

方法返回地址

一个方法开始后,只有两种方式可以退出,一种是当执行引擎遇到任意一个返回字节码指令的时候,另一种是在执行中遇到异常并且没有捕获的时候。无论以哪种方式退出,退出后都需要返回到方法被调用的位置,因此需要在栈帧中保存一些信息,用来恢复它上层方法的执行状态。

方法调用

方法调用不等于方法执行,方法调用阶段的唯一目的就是确定被调用方法的版本。

解析

我们知道,在虚拟机进行类加载的时候,有一个阶段叫做“解析”,在解析阶段会将常量池中的一部分符号引用转化为直接引用,在这个阶段,有一部分方法调用就已经被解析为了直接引用,解析的前提是,这部分方法的调用目标在编译阶段就可以确定下来。

在java虚拟机中共有5个方法调用指令:

invokestatic

invokespecial

invokevirtual

invokeinterface

invokedynamic

可以在“解析”阶段就确认调用目标的有invokestatic和invokespecial指令调用的方法以及invokevirtual调用的final方法,这些方法被称为“非虚方法”,与之相反的被称为“虚方法”。

分派

分派分为“静态分派”和“动态分派”。

静态分派

静态分派用来实现java语言的"重载"特性。

当我们声明一个变量时,可能会用到这种形式:Human man=new Man();(Man是Human子类),对于man这个变量来说,Human叫做它的静态类型,Man叫做它的实际类型。

"重载"是基于静态类型。也就是说,对于相同名称,参数列表不同的方法,选择哪个方法取决于参数列表中参数的静态类型,而静态类型在编译时是可知的,因此静态分派发生在编译阶段。

动态分派

动态分派用来实现java语言的“重写”特性,动态分派对应invokevirtual命令。

invokevirtual命令的执行过程如下:

找到操作数栈栈顶的第一个元素所指向的对象(也被称为方法的接收者)实际类型,记为C。

如果在C中找到与常量中的描述符和简单名称都相符的方法,则进行访问权限验证,如果通过则返回这个方法的直接引用,查找结束;如果不通过,则返回java.lang.IllegalAccessError异常。

否则,按照继承关系从下往上依次对C的各个父类进行第2步的搜索和验证过程。

如果最终没有找到合适的方法,则抛出java.lang.AbstractMethodError异常。

我们可以看出invokevirtual指令执行的时候是由对象的实际类型决定调用哪个方法版本的,而对象的实际类型只有到运行期的时候才能确定,我们把这种在运行期确定方法版本的分派过程称为“动态分派”。

单分派和多分派

方法的接收者与方法的参数统称为方法的宗量,根据分派基于宗量的多少,将分派分为单分派和多分派两种。

目前的java语言中,静态分派的时候,是基于接收者和方法参数两个宗量进行的,因此静态分派是多分派;动态分派的时候,是基于接收者一个宗量进行的,因此动态分派是单分派。

方法执行

一般从程序代码到物理机可以运行的目标代码都要经历下图所示的过程:

图中展示了编译执行和解释执行这两种路径,无论是哪种执行方式,一般都会先通过词法分析和语法分析把源代码转化为抽象语法树(AST)。

然后从抽象语法树节点开始,中间的分支代表的是解释执行过程,下边的分支代表的是编译执行过程。

jvm执行字节码是采用的解释执行,javac编译器完成了程序代码经过词法分析、语法分析到抽象语法树,再遍历语法树生成线性的字节码指令流的过程。而解释器则在虚拟机的内部。

javac编译器输出的字节码指令流是一种基于栈的指令集架构,指令流中的大部分指令都是零地址指令,它们依赖操作数栈进行工作。与之相对的是基于寄存器的指令集。

基于栈的指令集有以下优点:

可移植

代码更加紧凑(不需要存放参数)

编译器实现更简单(不需要考虑空间分配问题,所需空间都在栈上操作)

基于栈的指令集的主要缺点就是执行速度慢:一个是因为基于栈完成相同功能所需要的指令集数量一般比基于寄存器要多,因为入栈、出栈会产生多余的指令数量;另一个是因为栈是基于内存实现的,内存的读取速度本身就比处理器寄存器要慢,这一点可以采取栈顶缓存的手段,把最常用的操作映射到寄存器中避免直接访问内存,但是毕竟只是优化措施,不能解决本质问题。

执行方法中的代码本质就是通过栈的指令集来进行运算,这里就不详述了。

java虚拟机调用linux_Java虚拟机字节码执行引擎相关推荐

  1. JAVA 虚拟机类加载机制和字节码执行引擎

    引言 我们知道java代码编译后生成的是字节码,那虚拟机是如何加载这些class字节码文件的呢?加载之后又是如何进行方法调用的呢? 一 类文件结构 无关性基石 java有一个口号叫做一次编写,到处运行 ...

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

    字节码是什么东西? 以下是百度的解释: 字节码(Byte-code)是一种包含执行程序.由一序列 op 代码/数据对组成的二进制文件.字节码是一种中间码,它比机器码更抽象. 它经常被看作是包含一个执行 ...

  3. 《深入理解Java虚拟机》笔记5——类加载机制与字节码执行引擎

    第七章 虚拟机类加载机制 7.1 概述 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 在J ...

  4. java 虚拟机 字节码,JAVA虚拟机:虚拟机字节码执行引擎

    "虚拟机"是一个相对"物理机"的概念,这两种机器都有代码执行能力. 物理机的执行引擎是直接建立在处理器.硬件.指令集和操作系统层面上的. 虚拟机的执行引擎由自己 ...

  5. 深入理解Java虚拟机(周志明第三版)- 第八章:虚拟机字节码执行引擎

    系列文章目录 第一章: 走近Java 第二章: Java内存区域与内存溢出异常 第三章: Java垃圾收集器与内存分配策略 代码编译的结果从本地机器码转变为字节码,是存储格式发展的一小步,却是编程语言 ...

  6. 深入理解java虚拟机-第五章:虚拟机字节码执行引擎

    本章将介绍虚拟机如何调用方法 一.java虚拟机字节码执行引擎 执行引擎在执行代码的时候可能有解释执行(通过解释器执行)和编译执行(通过即时编译器产生本地代码执行)两种. 执行流程:输入的是字节码文件 ...

  7. JAVA类加载对字节码的处理_深入理解Java虚拟机(类文件结构+类加载机制+字节码执行引擎)...

    [本文版权归微信公众号"代码艺术"(ID:onblog)所有,若是转载请务必保留本段原创声明,违者必究.若是文章有不足之处,欢迎关注微信公众号私信与我进行交流!] 周志明的< ...

  8. 深入理解JVM虚拟机(七):虚拟机字节码执行引擎

    代码编译的结果就是从本地机器码转变为字节码.我们都知道,编译器将Java源代码转换成字节码?那么字节码是如何被执行的呢?这就涉及到了JVM字节码执行引擎,执行引擎负责具体的代码调用及执行过程.就目前而 ...

  9. jvm(8)-虚拟机字节码执行引擎

    [0]README 0.1)本文转自 "深入理解jvm",旨在学习 虚拟机字节码执行引擎 的基础知识: [1]概述 1)物理机和虚拟机的执行引擎: 物理机的执行引擎是直接建立在处理 ...

最新文章

  1. WPF之复杂形状控件
  2. Linux系统下如何挂载FAT32的U盘
  3. 31-32 python mysql-connector创建数据、crud,where,排序,删除等。PyMSQL驱动,插入操作、查询操作、更新操作、删除操作、执行
  4. R7-2 试试多线程 (10 分)
  5. MOSS 2007基础:WSS 3.0 中的母版页(Master Pages)和内容页(Content Pages)
  6. 2013 QConf上海软件开发大会总结
  7. android之Activity的生命周期
  8. 一篇极好的 CSS 教程
  9. 黄聪:php中utf-8模板获取GB2312编码页面传递URL中文编码转换方法
  10. android 4.3 模拟器,模拟器上的Android 4.3
  11. c-free5.0 程序代码正确 结果运行程序错误_Java程序员经典面试题集大全 (六)...
  12. TP-Link C2和C20i产品出现命令注入、DoS等多个漏洞 绿盟科技发布安全威胁通告
  13. 微信公众号支付失败的各种原因
  14. c语言编程用什么字体,10 款最适合编程的字体
  15. echarts-半圆
  16. html转图片/html2canvas的使用/星座测试/类似于损友圈的活动
  17. 选择 conforming 还是 non-conforming ?
  18. 淘宝店小蜜配置手册——自定义知识库的应答模式与测试
  19. 日更100天(42)每天进步一点点
  20. super-----this

热门文章

  1. Hadoop大数据——shuffle详解
  2. 如何使用IDEA 显示一个类的所有方法?与eclipse的outline视图类似
  3. QML on Android 在小米5s手机上中文字体显示异常
  4. 数组元素倒置-Java
  5. 快速弄懂内存字节对齐
  6. Git 之一 起源、安装、配置
  7. 算法练习day11——190329(平衡二叉树、搜索二叉树、完全二叉树)
  8. linux 4.4内核是什么,Linux 内核 4.4 LTS 将于2016年1月10日发布
  9. Linux虚拟地址空间布局以及进程栈和线程栈总结
  10. 高级工程师职英语计算机,高级工程师要考英语吗