【Java学习笔记(一百零七)】之字节码执行引擎,栈帧结构

2023-10-30

本文章由公号【开发小鸽】发布!欢迎关注!!!


老规矩–妹妹镇楼:

一. 字节码执行引擎

(一) 概述

       物理机和虚拟机都有代码执行能力,物理机的执行引擎建立在处理器,缓存机,指令集和操作系统之上,而虚拟机的执行引擎则是由软件实现的,不会受到物理条件制约地定制指令集与执行引擎的结构体系,能够执行哪些不被硬件支持的指令集格式。

(二) 虚拟机实现

       《规范》中制定了Java虚拟机字节码执行引擎的概念模型,但是对于不同的虚拟机实现中,执行引擎在执行字节码的时候,通常有解释执行(通过解释器执行)和编译执行(即时编译器产生本地代码执行)两种方式。执行引擎的输入都是字节码二进制流,处理过程是字节码解析执行的等效过程,输出的是执行结果。


二. 运行时栈帧结构

(一) 概述

       Java虚拟机以方法作为最基本的执行单元,栈帧是进行方法调用和方法执行的数据结构,也是虚拟机运行时数据区中虚拟机栈的栈元素。每一个方法从调用开始到执行结束,都对应着一个栈帧在虚拟机栈里入栈到出栈。

(二) 栈帧结构

       栈帧中存储着局部变量表,操作数栈,动态连接和方法返回地址等,在编译Java源码时,栈帧中需要多大的局部变量表,多深的操作数栈都已经计算出来了,并且写到方法表的Code属性中,即一个栈帧所需分配的内存不会受到运行时变量数据的影响,仅取决于程序源码和虚拟机栈内存布局。

       对于执行引擎来说,在活动线程中,只有位于栈顶的方法才是运行的,只有位于栈顶的栈帧才是生效的,称为当前栈帧,与这个栈帧关联的方法称为当前方法,执行引擎所运行的所有字节码指令都只针对当前栈帧。


(三) 局部变量表

1. 存储内容

       局部变量表示一组变量值的存储空间,用于存储方法参数和方法内部定义的局部变量,在程序源码被编译为Class文件时,方法的Code属性的max_locals数据项确定了该方法所需分配的局部变量表的最大容量。

       局部变量表的容量以变量槽为单位,对于32位以内的数据类型占用一个变量槽,对于64位的数据类型占用两个连续的变量槽。对于两个变量槽的读取操作的原子性问题,由于局部变量表是建立在线程堆栈上的,属于线程私有的数据,因此不会引起线程问题。

2. 索引

       Java虚拟机通过索引定位的方式使用局部变量表,索引值的范围从0开始到局部变量表的最大变量槽数量,如果访问的是32位数据类型,则索引N代表第N个槽;如果访问64位数据类型,则索引N表示第N和第N+1两个变量槽,虚拟机不允许采用任何方式单独地访问其中的一个。

       当方法被调用时,Java虚拟机会使用局部变量表完成参数值到参数变量列表的传递过程,即实参到形参的传递。如果调用的实例方法,则局部变量表中的第0位索引的变量槽默认是用于传递方法所属对象实例的引用,在方法中可以通过“this”访问到这个隐藏的参数。分配完参数后,在根据方法体内部定义的变量和作用域分配其他的变量槽。

3. 变量槽可重用

       局部变量表中的变量槽是可以重用的,方法体中定义的变量的作用域不一定会覆盖整个方法体,如果当前字节码PC计数器的值已经超过了某个变量的作用域,则该变量对应的变量槽可以交给其他变量来重用。不过,这样做可能对垃圾回收有影响,即使代码已经离开了某个对象的作用域,但是在此之后没有发生过对该变量所对应的局部变量表的变量槽的读写操作,该变量槽还没有被其他变量复用,因此作为GC Roots一部分的局部变量表仍然保持着对该对象的关联,因此无法回收这个对象。

       如何解决这个问题呢? 推荐的编码准则是将该变量用完后设置为null,这样该变量所对应的变量变量槽就会被清空,就可以被回收了。但是,通过即时编译器施加了各种编译优化后,就不需要这种设置为Null的操作了。

4. 局部变量的初始化

       对于类变量,是有两个阶段的赋初始值的操作的,一次是在准备阶段,赋予系统零值,另一次是在初始化阶段,赋予代码中的初始值。

       对于局部变量,只有代码中的初始值赋值操作,没有系统零值赋值,因此局部变量定义后一定要赋值,不然是无法使用的。

(四) 操作数栈

1. 存储内容

       同局部变量表一样,操作数栈的最大深度也在编译时写入到Code属性的max_stacks属性项中,操作数栈的每一个元素都可以是任意Java数据类型。32位的数据类型占一个栈容量,64位的数据类型占两个栈容量。Javac编译器的数据流分析保障了栈不会溢出。

2. 操作栈的作用

       当方法开始执行时,方法的操作数栈是空的,执行过程中,会有各种字节码指令往操作数栈中写入和提取内容。操作数栈的元素的数据类型必须与字节码指令的序列严格匹配,编译器一定要保证这一点,同时在类校验阶段的数据流分析也会校验。

(五) 动态连接

       每个栈帧都包含了一个指向运行时常量池中该栈帧所属方法的引用,为了方便支持调用过程中的动态连接。Class文件的常量池中有大量的符号引用,字节码的方法调用指令以符号引用为参数,一部分会在类加载阶段或者第一次使用时转为直接引用,这种称为静态解析;另一部分会在运行时转为直接引用,称为动态连接。

(六) 方法返回地址

       方法有两种退出方法的方式,一种是正常退出,另一种是异常退出。

1. 正常退出

       执行遇到任意一个方法返回的字节码指令,将返回值传递给上层的方法调用者。

2. 异常退出

       在方法执行的过程中遇到了异常,并且这个异常没有在方法体内妥善处理,只要在本方法的异常表中没有所有到匹配的异常处理器,就会导致方法退出,不会有任何返回值。

3. 恢复状态

       无论是哪种方法退出方式,退出之后都需要返回到最初方法被调用的位置,要实现这种方式,就需要在方法返回时在栈帧中保存一些信息,来恢复它的上层主调方法的执行状态。对于正常退出去,可以将主调方法的PC计数器的值作为返回地址,栈帧可以保存这个计数器的值;对于异常退出,返回地址是要通过异常处理器确定的,栈帧中一般不会保存这些信息。

4. 方法退出操作

       恢复上层方法的局部变量表和操作数栈,将返回值(有的话)压入调用者栈帧的操作数栈中,调整PC计数器的值指向方法调用指令后的一条指令。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Java学习笔记(一百零七)】之字节码执行引擎,栈帧结构 的相关文章

随机推荐

  • 基于stm32f1的内部读写flash

    flash是存储芯片的一种 通过特定的程序可以修改里面的数据 FLASH在电子以及半导体领域内往往表示Flash Memory的意思 即平时所说的 闪存 全名叫Flash EEPROM Memory 它结合了ROM和RAM的长处 不仅具备电
  • mysql5 autoreconnect_Mysql5的auto Reconnect错误

    一 解决方案一 最近在一个J2EE项目的开发过程中 遇到了这样的问题 在服务器上部署好这个Web系统后 这时访问系统是很正常的 当把服务器的时间 例如 2008 03 31 加一天或更多天 例如 2008 04 01 2008 04 02
  • 语义分割代码阅读---评价指标mIoU的计算

    1 语义分割IoU的定义 传统意义上的IoU Intersection over Union 交并比 直观表示 公式 语义分割中的IoU 在语义分割的问题中 这两个集合为真实值 ground truth 和预测值 predicted seg
  • react、vue项目代码阅读熟悉技巧

    在没有人给你解读项目的情况下 接手到老项目该如何开始起步熟悉呢 除了要大致到了解项目业务背景 技术栈 还应该要有通用化熟悉项目的技巧 每个人熟悉项目习惯技巧都不一样 尊重每个人的习惯和想法 建议收藏以下我的分享的熟悉项目技巧 拉项目 先熟悉
  • unity异常:InvalidOperationException: Burst failed to compile the function pointer `Int32

    异常信息具体如下 InvalidOperationException Burst failed to compile the function pointer Int32 ValidateCollinear BurstManaged Uni
  • 哈夫曼树以及哈夫曼编码的构造步骤

    注意 哈夫曼树并不唯一 但带权路径长度一定是相同的 第一部分 由给定结点构造哈夫曼树 1 8个结点的权值大小如下 2 从19 21 2 3 6 7 10 32中选择两个权小结点 选中2 3 同时算出这两个结点的和5 3 从19 21 6 7
  • C# Json的添加方式

    json的几种添加方式 1 只能添加JProperty类的Object 可以一次性添加多个 Add方法添加只能针对于json中的第一层 第二层添加目前还无法得知 但是有其他方法进行弥补 string json ReqCode errcode
  • 【报错】Python:WARNING: You are using pip version 21.1.3; however, version 23.0.1 is available.

    想使用pip安装Scrapy时 报了这个错误 WARNING You are using pip version 21 1 3 however version 23 0 1 is available You should consider
  • 《深入理解Java虚拟机 3》类加载机制与字节码执行引擎

    本系列是用来记录 深入理解Java虚拟机 这本书的读书笔记 方便自己查看 也方便大家查阅 欲速则不达 欲达则欲速 第六章 类文件结构 讲完了自动内存管理 我们来说说执行子系统 执行子系统讲解的是JVM如何执行程序 Class文件概述 这篇我
  • QEMU-KVM基本操作

    本文主要介绍KVM虚拟机的一些基本实践操作 对KVM虚拟机的管理操作主要是基于libvirt的命令行工具virsh进行的 一 安装与启动 1 KVM模块检查 1 查看当前Linux系统核心是否包含KVM模块 Linux内核2 6 20及以上
  • Cookie和Session

    Cookie和Session Cookie是浏览器给HTTP协议提供的一个持久化存储数据的方案 由于当前浏览器站在安全的角度考虑 不敢让页面直接来访问文件系统 Cookie是存储的键值对 不能存复杂的对象 只能存字符串 Cookie是按照域
  • [每周知识碎片] 2

    使用DistributedDataParallel 在Ctrl C 退出后留下许多僵尸进程 kill之后显卡掉了 类似情况1 2 解决方法 使用 ps aux grep python 查看python进程 然后按照顺序执行 kill 9 P
  • pycharm问题一(No module named 'selenium')

    pycharm上搭建python selenium自动化测试环境 背景 小白刚尝试摸门 明明安装了selenium pycharm中还是报No module named selenium 1 首先 竟然报没有selenium 就安装呗 cm
  • 期货开户金融市场非常残酷

    大多数交易者喜欢从零基础开始他们的交易旅程 毕竟 对于它们来说 点击鼠标确认交易真的非常简单 这个数字时代使我们易于交易 交易过程看起来非常简单 但是金融市场却非常残酷 在交易市场当中 所谓的 聪明 根本没有用 市场会一次又一次地给予你教训
  • 计算机vcruntime140.dll丢失的解决方法,重新安装教程

    vcruntime140 dll是Microsoft Visual C Redistributable文件中的一个动态链接库 DLL 这个文件是由Microsoft开发的 用于支持C 编程语言的运行环境 vcruntime140 dll是W
  • Linux的设置地区

    2023年7月21日 周五上午 本来想试试把这篇文章设置成VIP可见的 因为我挺好奇设置了VIP可见后会发生什么 但后来想想觉得这有违自己写博客的初心 于是就放弃了 我写博客的初心就是传递其他人写博客的那种无私的分享精神 为社会 中文社区和
  • python同一文件内class类的调用

    class 类名 def init self self a None self b None def 函数 self x y self x x self y y A 类名 B 类名 A 函数 10 20 print B x B y 将会输出
  • python实现二分查找的四种变体

    本文用python3实现了二分查找的四种变体 一 查找第一个值等于给定值的元素 二 查找最后一个值等于给定值的元素 三 查找第一个大于等于给定值的元素 四 查找最后一个小于等于给定值的元素 python3 一 查找第一个值等于给定值的元素
  • git rebase

    目录 一 开发分支落后于主干分支 个人修复用的分支落后于被修复分支 模拟环境 开始rebase操作 二 本地分支落后于远程分支 多人共用一个分支的情况下其他人有提交 在本地模拟环境 熟悉的可以跳过 比较啰嗦 详细步骤 开始模拟情景 解决 u
  • 【Java学习笔记(一百零七)】之字节码执行引擎,栈帧结构

    本文章由公号 开发小鸽 发布 欢迎关注 老规矩 妹妹镇楼 一 字节码执行引擎 一 概述 物理机和虚拟机都有代码执行能力 物理机的执行引擎建立在处理器 缓存机 指令集和操作系统之上 而虚拟机的执行引擎则是由软件实现的 不会受到物理条件制约地定