引入

class文件就是字节码文件,是由虚拟机执行的文件。也就是java语言和C & C++语言的区别就是,整个编译执行过程多了一个虚拟机这一步。这个在 类文件结构 中已经解释。上一节讲了虚拟机是如何加载一个class的,这一节就讲解虚拟机是如何执行class文件的。

运行时栈帧结构

1.定义

  • 栈是每个线程独有的内存。

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

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

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

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

2.栈帧结构

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

  • 在编译 程序代码的时候,栈帧中需要多大的局部变量表、多深的操作数栈都已经完全确定了,并且写入到方法表的Code属性之中,因此一个栈帧需要分配多少内存,不会受到程序运行期变量数据的影响,而仅仅取决于具体的虚拟机实现。

栈帧图解:

3.局部变量表

(1)定义:

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

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

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

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

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

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

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

  • this 引用(隐式参数)

  • 方法的参数表。

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

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

注意:局部变量只给予分配的内存,没有class对象的准备阶段,所以局部变量在使用前,必须先赋值(无初始值)。为了尽可能的节约局部变量表,Solt可以重用。

4.操作数栈

用来存放操作数的栈结构,当一个方法刚开始执行的时候,这个方法的操作数栈是空的,在方法的执行过程中,会有各种字节码指令向操作数栈中写入和提取内容,也就是入栈和出栈的操作。

注:java虚拟机的解释执行引擎称为基于栈的执行引擎,其中所指的栈就是操作数栈

5.动态连接

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

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

  • 还有一部分是在每一次的运行期间转化为直接引用,这个就是动态连接

6.方法返回地址

(1)方法被执行后,有两种方式退出这个方法

  • 执行引擎遇到任意一个方法的返回的字节码指令(如return)
  • 在方法执行过程中遇到了异常,并且这个异常并没有在方法体中得到处理。

(2)方法退出之后,需要返回到方法被调用的位置,程序才能继续执行,方法返回时需要在栈帧中保存一些信息,用以帮助它恢复它上层方法的执行状态。一般情况下,调用者的pc计数器的值可以作为返回地址,栈帧中很可能会保存这个计数器值,方法异常退出时,返回地址是要通过异常处理器表来确定,栈帧中一般不会保存这部分信息。

(3)方法退出的过程实际上等同于把当前栈帧出栈,所以可能需要执行这些操作:恢复上层方法的局部变量表和操作数栈,把返回值压入调用者栈的操作数栈中,调整pc计数器的值。

注:附加信息:虚拟机规范允许具体的虚拟机实现增加一些规范里没有描述的信息到栈帧中,这部分信息取决于具体的虚拟机实现。

7.方法调用

(1)方法调用阶段的唯一任务就是确定被调用方法的版本(即调用哪一个方法),暂时不涉及方法内部的具体运行过程。

(2)class文件的编译过程中不包含传统编译中的连接步骤,一切方法调用在class文件中存储的都是符号引用,而不是方法在实际运行时内存布局中的入口地址,这使得java有着更强大的动态扩展能力,但也使得java方法的调用过程变得相对复杂起来,需要在类的加载甚至运行期间才能确定目标方法的直接引用。

(3)两种方法调用

  • 解析调用:方法在程序真正运行之前就有一个可确定的调用版本,并且这个方法的调用版本在运行期是不可改变的

    • 符合这个条件的有静态方法,私有方法,实例构造器和父类方法四类,它们在类加载的时候会把符号引用解析为该方法的直接引用。

    • 解析调用一定是一个静态的过程,编译期间就完全确定,在类装载的解析阶段就会把涉及到的符号引用全部转化为可确定的直接引用,不会延迟到运行期间再去完成。

  • 分派调用:可能是静态的也可能是动态的,根据分派依据的宗量数又可分为单分派和多分派。分派机制与java的多态机制关系密切。

    • 静态分派:依赖静态类型来定位方法执行版本的分派动作。静态分派的最典型的应用就是方法重载。静态分派发生在编译阶段,因此确定静态分派的动作实际上不是由虚拟机来执行的

    • 动态分派:在运行期间根据实际类型来确定方法执行版本的分派调用过程称为动态分派。这跟多态性的另一个体现——重写有着很密切的关联。

    • 单分派:根据一个宗量对目标方法进行选择

    • 多分派:根据多于一个的总量对目标方法进行选择。

常用字节码

字节码指令是一个byte整数

(1) 局部变量压栈

  • Xload(x为i l f d a) a表示object ref

  • Xload_n (n为 0 1 2 3)将第几个局部变量的值压栈

  • Xaload(x为i f l d a b c s)从数组中取得给定索引的值,将该值压栈

(2) 出栈装载入局部变量

  • Xstore (x为i l f d a)出栈,存入局部变量

  • Xstore_n(n为0 1 2 3)出栈,将值存入第n个局部变量

  • Xastore(x为i f l d a b c s)将值存入数组

(3) 通用栈操作(无类型)

  • Nop

  • Pop 弹出栈顶1个字长

  • Dup 复制栈顶一个字长,复制内容压栈

(4) 类型转化

  • I2l i2f l2i l2f l2d f2i f2d d2i d2l d2f i2b i2c i2s

  • i2l表示将int转为long

(5) 整数运算

  • Iadd lsub idiv imul linc

(6) 浮点运算

  • Fadd dadd fsub fdiv ddiv fmul dmul

(7) 对象操作指令

  • New getfield putfield getstatic putstatic

(8) 条件控制

  • Ifeq 若为0,则跳转,

  • ifne 若不为0,则跳转

  • iflt 若小于0,则跳转,

  • ifge若大于0,则跳转

  • if_icmpeq 如果两个int相同,则跳转

(9) 方法调用

  • Invokevirtual

  • Invokespecial

  • Invokestatic

  • Invokeinterface

  • xreturn

(10) 常量入栈

  • Aconst_null null对象入栈

  • Iconst_m1 int常量-1入栈

  • Iconst_0 int常量0入栈

  • Lconst_1 long常量1入栈

  • Fconst_1 float常量1.0入栈

  • Dconst_1 double常量1.0入栈

  • Bipush 8位带符号整数入栈

  • Sipush 16位带符号整数入栈

  • Idc 常量池中的项入栈

说了这么多,下面来个例子实战一下

简单的字节码执行实例

例:Java程序

public class TestFile {
public int calc() {
int a = 500;
int b = 200;
int c = 50;
return (a + b) / c;
}
}

执行 javap –verbose TestFile 命令生成反汇编代码如下:

public int testfile();
Code:
Stack=2, Locals=4, Args_size=1
0: sipush 500
3: istore_1
4: sipush 200
7: istore_2
8: bipush 50
10: istore_3
11: iload_1
12: iload_2
13: iadd
14: iload_3
15: idiv
16: ireturn
}

执行过程

1.将500入栈,然后出栈将500存入第一个局部变量

2.将200入栈,然后出栈将200存入第二个局部变量

3.将50入栈,然后将50存入第三个局部变量

4.将第一个局部变量的值(500)压栈,然后将第二个局部变量的值(200)也压栈

5.执行整型加法运算,然后将第三个局部变量的值(50)压栈

6.执行整型除法运算,最后执行整型的返回操作将值返回。

本人才疏学浅,若有错,请指出,谢谢!
如果你有更好的建议,可以留言我们一起讨论,共同进步!
衷心的感谢您能耐心的读完本篇博文。

文章知识点与官方知识档案匹配,可进一步学习相关知识
Java技能树首页概览106421 人正在系统学习中

[转帖]【JVM】字节码执行引擎的更多相关文章

  1. JVM总结(五):JVM字节码执行引擎

    JVM字节码执行引擎 运行时栈帧结构 局部变量表 操作数栈 动态连接 方法返回地址 附加信息 方法调用 解析 分派 –“重载”和“重写”的实现 静态分派 动态分派 单分派和多分派 JVM动态分派的实现 ...

  2. 一夜搞懂 | JVM 字节码执行引擎

    前言 本文已经收录到我的 Github 个人博客,欢迎大佬们光临寒舍: 我的 GIthub 博客 学习导图 一.为什么要学习字节码执行引擎? 代码编译的结果从本地机器码转变为字节码,是存储格式发展的一 ...

  3. 深入理解JVM—字节码执行引擎

    原文地址:http://yhjhappy234.blog.163.com/blog/static/3163283220122204355694/ 前面我们不止一次的提到,Java是一种跨平台的语言,为 ...

  4. JVM字节码执行引擎和动态绑定原理

    1.执行引擎 所有Java虚拟机的执行引擎都是一致的: 输入的是字节码文件,处理过程就是解析过程,最后输出执行结果. 在整个过程不同的数据在不同的结构中进行处理. 2.栈帧 jvm进行方法调用和方法执 ...

  5. JVM字节码执行引擎

    一.概述 在不同的虚拟机实现里面,执行引擎在执行Java代码的时候可能会有解释执行(通过解释器执行)和编译器执行(通过即时编译器产生本地代码执行)两种选择,所有的Java虚拟机的执行引擎都是一致的:输 ...

  6. 图解JVM字节码执行引擎之栈帧结构

    一.执行引擎      “虚拟机”的概念是相对于“物理机”而言的,这两种“机器”都有执行代码的能力.物理机的执行引擎是直接建立在硬件处理器.物理寄存器.指令集和操作系统层面的:而“虚拟机”的执行引擎是 ...

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

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

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

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

  9. JVM基础结构与字节码执行引擎

    JVM基础结构 JVM内部结构如下:栈.堆. 栈 JVM中的栈主要是指线程里面的栈,里面有方法栈.native方法栈.PC寄存器等等:每个方法栈是由栈帧组成的:每个栈帧是由局部变量表.操作数栈等组成. ...

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

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

随机推荐

  1. 听6位专家畅谈AI大模型落地实践:场景和人才是关键

    本文分享自华为云社区<听6位专家畅谈AI大模型落地实践:场景和人才是关键>,作者:华为云社区精选. 随着人工智能技术的不断突破,越来越多的行业开始应用AI技术来提高效率.降低成本.改善用户 ...

  2. KubeFlow-Pipeline及Argo实现原理速析

    Argo是一个开源原生容器工作流引擎用于在Kubernetes上开发和运行应用程序.Argo Workflow流程引擎,可以编排容器流程来执行业务逻辑,在20年4月8日进入CNCF孵化器组. 而Kub ...

  3. 华为云IoT智简联接,开启物联世界新纪元

    摘要:华为云IoT将聚焦物联网技术和商业基础能力建设,联接万物.联接生态.联接行业,帮助各行各业做好数字化转型. 近日,华为云通过线上专题演讲发布了IoT最新战略.华为云IoT将聚焦物联网基础能力(包 ...

  4. 教你VUE中的filters过滤器2种用法

    摘要:Vue.js 允许我们自定义过滤器,可被用于一些常见的文本格式化. 本文分享自华为云社区<VUE中的filters过滤器用法>,作者:小小张自由--张有博. 前言 Vue.js 允许 ...

  5. DataLeap的Catalog系统近实时消息同步能力优化

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 摘要 字节数据中台DataLeap的Data Catalog系统通过接收MQ中的近实时消息来同步部分元数据.Apa ...

  6. zsh踩坑记录

    1. zsh: no matches found: uvicorn[standard] 方法一 # 在~/.zshrc中添加下面这句话 setopt no_nomatch # 然后source ~/. ...

  7. 【JAVA基础】Swagger使用

    Swagger使用 刷新权限 自定标签名称

  8. Qt 的Cmake方式如何创建资源文件和添加类

    CLion(误,QT Creator) 添加资源文件时 选择Qt 然后选择Qt Resource File 单击choose,然后给你的资源文件输入一个名字比如res单击下一步,然后完成,保存 这时候 ...

  9. HDU-3032--Nim or not Nim?(博弈+SG打表)

    题目分析: 这是一个经典的Multi-SG游戏的问题. 相较于普通的Nim游戏,该游戏仅仅是多了拆成两堆这样的一个状态.即多了一个SG(x+y)的过程. 而根据SG定理,SG(x+y)这个游戏的结果可 ...

  10. Ipa Guard使用手册

    ​ 使用手册 开始使用ipa guard 代码混淆界面介绍 文件混淆-界面介绍 安装和登录Ipa Guard 相关教程 下载安装Ipa Guard ipaguard注册和登录 下载安装Ipa Guar ...