一、运行时栈帧结构

栈帧是用于支持虚拟机进行方法调用和执行的数据结构,是虚拟机栈的栈元素。

栈帧存储了局部变量表,操作数栈,动态连接,和返回地址等。

每一个方法的执行 对应的一个栈帧在虚拟机里面从入栈到出栈的过程。

只有位于栈顶的栈帧才有有效的,对应的方法称为当前方法。

执行引擎运行的所有指令只针对当前栈帧和当前方法。

1.局部变量表

局部变量表存放的一组变量的存储空间。存放方法参数和方法内部定义的局部变量表。

在java编译成class的时候,已经确定了局部变量表所需分配的最大容量。

局部变量表的最小单位是一个Slot。

虚拟机规范没有明确规定一个Slot占多少大小。只是规定,它可以放下boolean,byte,...reference &return address.

reference 是指一个对象实例的引用。关于reference的大小,目前没有明确的指定大小。但是我们可以理解为它就是类似C++中的指针。

局部变量表的读取方式是索引,从0开始。所以局部变量表可以简单理解为就是一个表.

局部变量表的分配顺序如下:

  • this 引用。可以认为是隐式参数。
  • 方法的参数表。

根据局部变量顺序,分配Solt。

一个变量一个solt,64为的占2个solt。java中明确64位的是long & double

为了尽可能的节约局部变量表,Solt可以重用。

注意:局部变量只给予分配的内存,没有class对象的准备阶段,所以局部变量在使用前,必须先赋值。

2.操作数栈

操作数栈在概念上很像寄存器。

java虚拟机无法使用寄存器,所以就有操作数栈来存放数据。

虚拟机把操作数栈作为它的工作区——大多数指令都要从这里弹出数据,执行运算,然后把结果压回操作数栈。

比如,iadd指令就要从操作数栈中弹出两个整数,执行加法运算,其结果又压回到操作数栈中,看看下面的示例,

它演示了虚拟机是如何把两个int类型的局部变量相加,再把结果保存到第三个局部变量的:

begin

iload_0 // push the int in local variable 0 onto the stack

iload_1 // push the int in local variable 1 onto the stack

iadd // pop two ints, add them, push result

istore_2 // pop int, store into local variable 2

end

操作数栈 的数据读取、写入就是出栈和如栈操作。

3.动态连接

每个栈帧都包含一个指向运行时常量池的引用,持有这个引用是为了支持动态连接。

符号池的引用,有一部分是在第一次使用或者初始化的时候就确定下来,这个称为静态引用。

还有一部分是在每次执行的时候采取确定,这个就是动态连接。

4.方法返回地址

方法只有2中退出方式,正常情况下,遇到return指令退出。还有就是异常退出。

正常情况:一般情况下,栈帧会保存 在程序计数器中的调用者的地址。虚拟机通过这个方式,执行方法调用者的地址,

然后把返回值压入调用者中的操作数栈。

异常情况:方法不会返回任何值,返回地址有异常表来确定,栈帧一般不存储信息。

二、方法调用

方法调用阶段不是执行该方法,而仅仅时确认要调用那个方法。class文件在编译阶段没有连接这一过程,、

所以动态连接这个在C++就已经有的技术,在java运用到了一个新的高度。所有的函数(除了私有方法,构造方法 & 静态方法,下同),理论上

都可以时C++里面的虚函数。所以所有的函数都需要通过动态绑定来确定“明确”的函数实体。

  解析

所有方法调用的目标方法都是常量池中的符号引用。在类的加载解析阶段,会将一部分目标方法转化为直接引用。(可以理解为具体方法的直接地址)

可以转化的方法,主要为静态方法 & 私有方法(编译期可知,运行期不可变)。

Java虚拟机提供5中方法调用命令:

invokestatic:调用静态方法

invokespecial:调用实例构造器,私有方法和父类方法

invokevirtual:调用虚方法

invokeinterface:调用接口方法

invokedynamic:现在运行时动态解析出该方法,然后执行。

invokestatic & invokespecial 对应的方法,都是在加载解析后,可以直接确定的。

所以这些方法为非虚方法。

java规定 final修饰的是一种非虚方法。

  分派

1.静态分派

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

静态分派实际上发生在方法编译阶段,故此动作实际上不是虚拟机执行的。

Human man = new Man();

这里的Human我们理解为静态类型,后面的Man是实际类型。我们在编译器只知道静态类型,后面的实际类型等到动态连接的时候才知道。

虚拟机在重载时,是通过参数的静态类型,而不是实际类型来判断使用那个方法的。

2.动态分派

在运行期间根据实际类型确定方法执行版本的分派过程称为动态分派。,典型应用是方法重写

先从当前类里面寻找“同名”的该方法,如果没有,就从下往上从其各个父类里面找,知道找到为止!

这个找到的方法,就是我们实际要调的方法。如果找不到,就是exception。一般情况下,编译工具会帮我们避免这种情况。

3.单分派与多分派

方法接收者与方法的参数统称为方法的宗量(方法的版本与方法参数)

静态分派过程中需要确定静态类型与方法参数的类型,所以静态分派过程为多分派。

方法的选择是在静态编译的时候就确认的(通过静态类型),只是需要进一步确认实体类型。所以动态分派是单分派。

  动态类型语言支持:

使用C++语言可以定义一个调用方法:

void sort(int list[],const int size,int (*compare)(int,int));

但是java很难做到这一点,

void sort(List list,Compare c);Compare 一般要用接口实现。

在java 1.7 有一种方法可以支持该功能 MethodHandle,在虚拟机层面实现动态类型支持,方便运行其他语言。一个小应用就是可以调用到祖类方法。

总体来说,静态类型更严谨,动态类型更灵活。

三、

Java编译器输出的指令流,基本上是一种基于栈的指令集架构(Instruction Set Architecture,ISA),指令流中的指令大部分都是零地址指令,它们依赖操作数栈进行工作。

与之相对的另外一套常用的指令集架构是基于寄存器的指令集。

基于栈的指令集主要的优点就是

可移植,寄存器由硬件直接提供,程序直接依赖这些硬件寄存器则不可避免地要受到硬件的约束。

栈架构的指令集还有一些其他的优点,如代码相对更加紧凑(字节码中每个字节就对应一条指令,而多地址指令集中还需要存放参数)

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

栈架构指令集的主要缺点是

执行速度相对来说会稍慢一些,出栈、入栈操作产生了相当多的指令数量,

频繁的内存访问。

jvm虚拟机笔记<四> 虚拟机字节码执行引擎的更多相关文章

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

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

  2. 深入理解Java虚拟机06--虚拟机字节码执行引擎

    一.前言 物理机的执行引擎是直接在物理硬件如CPU.操作系统.指令集上运行的,但是对于虚拟机来讲,他的执行引擎由自己实现. 执行引擎有统一的外观(Java虚拟机规范),不同类型的虚拟机都遵循了这一规范 ...

  3. 深入理解JVM虚拟机5:虚拟机字节码执行引擎

    虚拟机字节码执行引擎   转自https://juejin.im/post/5abc97ff518825556a727e66 所谓的「虚拟机字节码执行引擎」其实就是 JVM 根据 Class 文件中给 ...

  4. 深入理解Java虚拟机读书笔记5----虚拟机字节码执行引擎

    五 虚拟机字节码执行引擎   1 运行时栈帧结构     ---栈帧是用于支持虚拟机进行方法调用和方法执行的数据结构,是虚拟机运行时数据区中的虚拟机栈的栈元素.     ---栈帧中存储了方法的局部变 ...

  5. 深入理解Java虚拟机(类文件结构+类加载机制+字节码执行引擎)

    目录 1.类文件结构 1.1 Class类文件结构 1.2 魔数与Class文件的版本 1.3 常量池 1.4 访问标志 1.5 类索引.父索引与接口索引集合 1.6 字段表集合 1.7 方法集合 1 ...

  6. 【java虚拟机系列】从java虚拟机字节码执行引擎的执行过程来彻底理解java的多态性

    我们知道面向对象语言的三大特点之一就是多态性,而java作为一种面向对象的语言,自然也满足多态性,我们也知道java中的多态包括重载与重写,我们也知道在C++中动态多态是通过虚函数来实现的,而虚函数是 ...

  7. java虚拟机字节码执行引擎

    定义 java虚拟机字节码执行引擎是jvm最核心的组成部分之一,它做的事情很简单:输入的是字节码文件,处理过程是字节码解析的等效过程,输出的是执行结果.在不同的虚拟机实现里,执行引擎在执行java代码 ...

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

    深入理解Java虚拟机(字节码执行引擎) 本文首发于微信公众号:BaronTalk 执行引擎是 Java 虚拟机最核心的组成部分之一.「虚拟机」是相对于「物理机」的概念,这两种机器都有代码执行的能力, ...

  9. JVM学习笔记:字节码执行引擎

    JVM学习笔记:字节码执行引擎 移步大神贴:http://rednaxelafx.iteye.com/blog/492667  

  10. 《深入理解Java虚拟机》-----第8章 虚拟机字节码执行引擎——Java高级开发必须懂的

    概述 执行引擎是Java虚拟机最核心的组成部分之一.“虚拟机”是一个相对于“物理机”的概念 ,这两种机器都有代码执行能力,其区别是物理机的执行引擎是直接建立在处理器.硬件.指令集和操作系统层面上的,而 ...

随机推荐

  1. 2016/09/29 Maven简介

    1.1 何为Maven Maven这个词可以翻译为"知识的积累",也可以翻译为"专家"或"内行".作为Apache组织中的一个颇为成功的开源 ...

  2. Intent知识详解

    Intent知识详解 一.什么是Intent 贴一个官方解释: An intent is an abstract description of an operation to be performed ...

  3. Java并发之synchronized关键字深度解析(三)

    前言 本篇主要介绍一下synchronized的批量重偏向和批量撤销机制,属于深水区,大家提前备好氧气瓶. 上一篇说完synchronized锁的膨胀过程,下面我们再延伸一下synchronized锁 ...

  4. sqlserver 行转列、字符串行转列、自动生产行转列脚本

    行转列,老生常谈的问题.这里总结一下网上的方法. 1.生成测试数据: CREATE TABLE human( name ), --姓名 norm ), --指标 score INT , --分数 gr ...

  5. python 多线程编程之_thread模块

    参考书籍:python核心编程 _thread模块除了可以派生线程外,还提供了基本的同步数据结构,又称为锁对象(lock object,也叫原语锁.简单锁.互斥锁.互斥和二进制信号量). 下面是常用的 ...

  6. [译]C# 7系列,Part 4: Discards 弃元

    原文:https://blogs.msdn.microsoft.com/mazhou/2017/06/27/c-7-series-part-4-discards/ 有时我们想要忽略一个方法返回的值,特 ...

  7. Sql将一列数据拆分为多行显示的两种方法

    原始数据与期望结果有表tb, 如下:id          value----------- -----------1           aa,bb2           aaa,bbb,ccc欲按 ...

  8. MyBatis进阶讲解+ssm集成

    1.sql映射器Mapper MyBatis基于动态代理机制,让我们无需再编写Dao的实现. 传统Dao接口,现在名称统一以Mapper结尾,还有我们映射器配置文件要和映射器在同一个包. 1.1使用映 ...

  9. 林克的小本本之——HCL网络知识随笔

    单臂路由 单臂路由是一种通过在路由器的一个接口上配置逻辑接口,来实现不同Vlan之间互通的方法. 实例: #配置SW1的VLAN [SW1]vlan 2 #配置端口vlan略 [SW1]int g1/ ...

  10. ZKWeb 官网与演示站点的部署步骤 (Linux + Nginx + Certbot)

    因为没有给域名续费,加上私人时间不足,ZKWeb 的官网和演示站点已经停止了几个月的时间. 最近时间开始变多,所以重新购买了别的域名和服务器把官网和演示站点重新部署上去. 在此前站点是托管在共享主机上 ...