JVM的故事——虚拟机字节码执行引擎

news2024/11/18 3:31:41

虚拟机字节码执行引擎

文章目录

  • 虚拟机字节码执行引擎
  • 一、概述
  • 二、运行时栈帧结构
  • 三、方法调用


一、概述

执行引擎Java虚拟机的核心组成之一,它是由软件自行实现的,能够执行那些不被硬件直接支持的指令集格式。
对于不同的虚拟机实现,执行引擎可能会有解释执行和编译执行或者两种兼备,但是所有执行引擎的输入输出都是一样的,输入的是字节码二进制流,输出的是执行结果

二、运行时栈帧结构

Java虚拟机以方法为最基本的执行单元,栈帧则是虚拟机用于方法调用和方法执行背后的数据结构,它是虚拟机运行时数据区中的虚拟机栈的基本元素。栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息。一个栈帧需要分配多少内存具体虚拟机实现的栈内存布局形式。
以Java程序的角度来看,同一时刻,同一线程里面在调用堆栈的所有方法都处于执行状态。以执行引擎的角度来看,只有位于栈顶的栈帧才是生效的,其被称为当前栈帧,对应的方法被称为当前方法。栈帧结构如图8-1所示。
在这里插入图片描述
(一)局部变量表
局部变量表用于存放方法参数和方法内的局部变量,在Java程序被编译为class文件时,就在方法的Code属性的max_locals数据项中确定了该方法所需分配的局部变量表的最大容量。
局部变量表的容量以变量槽为最小单位,变量槽的大小并没有明确规定,只是说每个变量槽都需要可以存放boolean、 byte、char、short、int、float、reference或returnAddress类型的变量,这8种数据类型最大是32位。所以变量槽需要大于32位,但不一定是32位。
对于提到的8种数据类型,前6种和Java中的差不多,reference表示对一个对象实例的引用,returnAddress指向了一条字节码指令的地址,这种类型已经很少见了。像long和double这样64位的数据类型,Java虚拟机会以高位对齐的方法为其分配两个连续的变量槽空间。
Java虚拟机通过索引定位的方式使用局部变量表,若是32位的数据类型,索引N就对应着第N个变量槽,若是64位的数据类型,索引N就对应着第N和第N+1个变量槽。对于两个相邻的共同存放64位数据类型变量的变量槽,不允许单独访问某一个变量槽。
当一个方法被调用时,就会使用局部变量表来完成参数值到参数变量列表的传递过程。
为了节省栈帧所占用的内存空间,局部变量表中的变量槽是可以重用的。若当前字节码PC计数器已经超过了方法体内的某个变量计数范围,那么这个变量的变量槽就可以被其它变量重用。但有时候变量槽的复用会影响到垃圾收集。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
代码8-1和8-2中的placeholder占用的空间都没有被回收,只有代码8-3的palceholder占用的空间被回收了。这是因为判断是否被回收的根本原因是:局部变量表中的变量槽是否还存在对placeholder的引用。8-1是因为执行System.gc的时候还在placeholder的作用域中;8-2是因为placeholder原本所占用的变量槽还没有被其它变量复用。8-3中,已经不在placeholder的作用域中了,并且int a=0复用了placeholder原本占用的变量槽。在这里如果手动给placeholder赋null值也是一样的,但是赋null值在经过即使编译优化后是会被当作无效操作消除掉的。
局部变量不像前面提到的类变量存在准备阶段,前文的类变量会在准备阶段被赋一个系统初始值,然后在初始化阶段被赋一个定义的初始值,所以即使代码中没有给类变量赋初始值也是可以的。但是局部变量如果定义了但没赋初始值,它就是完全不能使用的。

(二)操作数栈
操作数栈和局部变量表类似,也是在编译期间最大深度就写入了Code属性的max_stacks数据项之中。32位数据类型占一个栈帧,64位数据类型占两个栈帧。
在方法执行时,是先把运算涉及的操作数压入到栈中,然后调用运算指令,使对应的数出栈进行运算,再把结果入栈。栈中的元素类型与字节码指令指令需要严格匹配,如iadd指令,栈顶的两个元素必须是两个int类型的。
在概念模型中,两个不同栈帧作为不同虚拟机栈的元素,是完全独立的,但是大部分虚拟机实现中,栈帧是有重叠部分的。如图所示。
在这里插入图片描述
(三)动态连接
每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。在类加载阶段或者第一次使用符号引用被转化为直接引用,这是静态解析。在运行时符号引用转化为直接引用,这是动态连接。

(四)方法返回地址
当一个方法开始执行后,只有两种方式退出这个方法。第一种就是遇到一个方法返回的字节码指令,这种方式叫做”正常调用完成”。第二种就是遇到异常,并且还没有处理好遇到的异常,这种方式叫做”异常调用完成”。
无论采用哪种退出方式,方法退出后都必须回到最初方法被调用的位置。
方法退出过程等同于把当前栈帧出栈,因此退出时可能执行的操作有:恢复上层方法的局部变量表和操作数栈,把返回值压入调用者的操作数栈中,调整PC计数器的值以指向后一条指令。

(五)附加信息
在讨论概念时,一般会把动态连接、方法返回地址与其他附加信息全部归为一类,称为栈帧信息。

三、方法调用

方法调用并不涉及到具体的执行,只是确定方法的版本(调用哪个方法)。一切方法调用在class文件中只是符号引用,而不是实际运行时内存布局中的入口地址(直接引用)。这使得某些类在类加载期间甚至是运行期间才能确定目标方法的直接引用。
(一)解析
在类加载阶段,就有一部分符号引用转化为直接引用,这个前提是方法在程序运行前就有一个确定的版本。这类方法的调用被称为解析。
在Java中符合“编译器可知,运行期不可变”的,主要有静态方法和私有方法。
调用不同类型的方法,字节码指令集中有不同的指令:
invokestatic:调用静态方法
invokespecial:调用实例构造器方法、父类方法和私有方法
invokevirtual:调用虚方法
invokeinterface:调用接口方法
invokedynamic:先动态解析出调用点限定符所引用的方法,再执行该方法。
只要能被invokestatic和invokespecial调用的方法,都可在解析阶段确定唯一版本。有静态方法、私有方法、类的构造方法、父类方法四种,还有被final修饰的方法,不过被final修饰的方法被invokevirtual调用。这五种方法在类加载阶段就把符号引用替换为直接引用,它们被称为“非虚方法”,其它的方法被称为“虚方法”。
在这里插入图片描述
如图,静态方法sayHello只属于该类型,没有任何方式覆盖或者隐藏这个方法。
用javap指令查看字节码,发现的确是通过invokestatic方法调用的sayHello()方法

(二)分派
Java是一门面向对象的程序语言,它具备面向对象的3个基本特征:封装、继承、多态。本节的分派调用过程将会揭多态的一些基本体现。
1.静态分派
分派这个词本来就具有动态性,在书中英文是“Method Overload Resolution”,即应该属于8.2中的解析。不过很多中文资料都称这种行为为静态分派。
在这里插入图片描述
运行结果为: hello,guy!
hello,guy!
对于变量man和woman来说,Human是静态类型,而对应的Man和Woman是运行时类型(实际类型)。变量最终的静态类型在编译期可知,而实际类型在运行期才可以确定。
虚拟机在重载时是通过参数的静态类型作为依据的,在编译期间就根据参数的静态类型选择了调用的方法。静态分派最典型的就是重载,这发生在编译期间,所以实际上静态分派动作并不是由虚拟机执行的。
重载方法匹配并不一定就是完全对应的,也会自动转换,这个转换是有优先级的。比如字符’a’,它的重载匹配的方法是按照参数类型为char>int>long>float>double的顺序转型进行匹配。如果都没有,就会进行自动装箱,匹配到Character。把Character类型的参数再注释掉,会匹配Character实现的接口Serializable和 Comparable。再没有也有可能会匹配到父类Object。

2.动态分派
静态分派是和重载有着很大的关系,动态分派是和重写有着很大的关系。
在这里插入图片描述
运行结果是man say hello
woman say hello
woman say hello

代码中的两个变量静态类型都是Human,实际类型是Man和Woman,变量man的实际类型在之后还变成了Woman。
在这里插入图片描述
根据字节码,可以看出两个调用无论指令还是参数都一样,但是最终执行的目标方法不同,那么就是invokevirtual有着一些判断。
invokevirtual的解析过程大致为下面几步:
1)找到操作数栈顶的第一个元素所指向的对象的实际类型,记作C。
2)如果在类型C中找到与常量中的描述符和简单名称都相符的方法,则进行访问权限校验,如果通过则返回这个方法的直接引用,查找过程结束;不通过则返回java.lang.IllegalAccessError异常。
3)否则,按照继承关系从下往上依次对C的各个父类进行第二步的搜索和验证过程。
4)如果始终没有找到合适的方法,则抛出java.lang.AbstractMethodError异常。
所以说invokevirtual还会根据方法接收者的实际类型去选择方法版本。这个过程就是Java重写的本质,这种分派过程就是动态分派。
多态性的根源在于虚方法调用invokevirtual,所以字段是没有多态的。当子类有与父类同名字段,虽然内存中两个字段都存在,但实际上子类会覆盖父类的字段。
在这里插入图片描述
在这里插入图片描述
结果分析:首先子类隐式调用了父类的构造方法,父类构造方法调用的showMeTheMoney是虚方法,虚方法看的是实际类型,所以其实是Son::showMeTheMoney,这个时候Son的money字段还为0,输出了第一行。然后就到了Son的构造方法的调用,输出了第二行。主方法中访问money字段,看的是静态类型,所以是Father中的2。

3.单分派与多分派
方法的接收者与方法的参数统称为方法的宗量,根据宗量的多少可以把方法分为单分派和多分派。
在这里插入图片描述
在这里插入图片描述
编译阶段编译器选择的过程,也就是静态分派的过程。这时候选择目标的根据:1、静态类型是Father还是Son 2、参数类型是360还是QQ。这根据了两个宗量进行选择,所以静态分派过程是多分派。
运行阶段虚拟机的选择,也就是动态分派的过程。在执行“son.hardChoice(new QQ())”使,已经确定参数类型是QQ了,所以唯一影响选择的就是接收者的实际类型是Father还是Son。只根据了一个宗量进行选择,所以动态分派过程是单分派。
所以Java语言是一门静态多分派,动态单分派的语言。

4.虚拟机动态分派的实现
动态分派是执行非常频繁的动作,所以真正运行时不会如此频繁的去搜索元数据,而是会建立一个虚方法表。
在这里插入图片描述
虚方法表中存放着各个方法的实际入口地址,如果某个方法在子类中没有被重写,那么它在子类和父类的地址是一样的,都指向父类的实现入口。如图8-3,Son重写了Father的全部方法,所以没有指向Father的箭头,但是他们俩都没有重写Object的方法,所以都有指向Object的箭头。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/956528.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

css强制显示一行

要强制将文本内容显示在一行中,可以使用CSS的white-space属性和overflow属性来实现。 首先,将white-space属性设置为nowrap,这样文本内容就不会换行。然后,将overflow属性设置为hidden,这样超出一行的内容就会被隐藏起…

从零开始探索C语言(五)----函数和作用域

文章目录 1. 函数1.1 函数定义1.2 函数声明1.3 函数调用1.3.1 传值调用1.3.2 引用调用 2. 作用域2.1 局部变量2.2 全局变量2.3 形式参数 1. 函数 函数是一组一起执行一个任务的语句。每个 C 程序都至少有一个函数,即主函数 main() ,所有简单的程序都可以…

直接插入排序与希尔排序

目录 一,排序的概念 二,插入排序 2.1直接插入排序 2.2 希尔排序 一,排序的概念 排序:所谓排序,就是使一串记录,按照其中的某个或某些或某些关键字的大小,递增或递减的排列 稳定性&#xff…

flutter自定义按钮-文本按钮

目录 前言 需求 实现 前言 最近闲着无聊学习了flutter的一下知识,发现flutter和安卓之间,页面开发的方式还是有较大的差异的,众所周知,android的页面开发都是写在xml文件中的,而flutter直接写在代码里(da…

无重叠区间【贪心算法】

无重叠区间 给定一个区间的集合 intervals ,其中 intervals[i] [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠 。 class Solution {public int eraseOverlapIntervals(int[][] intervals) {//先排序,按照左边界升序,注…

Vue3 select循环多个,选项option不能重复被选

Vue3 select循环多个,选项option不能重复被选 环境:vue3tsviteelement plus 实现目标:Vue3 select循环多个,当其中一个option值被选后,其他select里面不能再重复选择该option值。第二种,当其中一个option值…

雅思写作 三小时浓缩学习顾家北 笔记总结(二)

目录 饥饿网一百句翻译 Using government funds for pollution cleanup work can create a comfortable environment. "Allocating government funds to pollution cleanup work can contribute to the creation of a comfortable environment." Some advertise…

Linux的基本使用和Web程序部署(JavaEE初阶系列18)

目录 前言: 1.Linux 1.1Linux是什么 1.2Linux发行版 1.3Linux环境搭建 1.3.1环境搭建方式 1.3.2使用云服务器 1.4使用终端软件连接到Linux 1.4.1什么是终端软件 1.4.2使用Xshell登录主机 1.5Linux常用的命令 1.5.1ls 1.5.2cd 1.5.3pwd 1.5.4touch 1.…

vscode html使用less和快速获取标签less结构

扩展插件里面搜索 css tree 插件 下载 使用方法 选择你要生成的标签结构然后按CTRLshiftp 第一次需要在输入框输入 get 然后选择 Generate CSS tree less结构就出现在这个里面直接复制到自己的less文件里面就可以使用了 在html里面使用less 下载 Easy LESS 插件 自己创建…

Python爬虫-某网酒店数据

前言 本文是该专栏的第5篇,后面会持续分享python爬虫案例干货,记得关注。 本文以某网的酒店数据为例,实现根据目标城市获取酒店数据。具体思路和方法跟着笔者直接往下看正文详细内容。(附带完整代码) 正文 地址:aHR0cHM6Ly93d3cuYnRoaG90ZWxzLmNvbS9saXN0L3NoYW5naGFp …

ceph源码阅读 erasure-code

1、ceph纠删码 纠删码(Erasure Code)是比较流行的数据冗余的存储方法,将原始数据分成k个数据块(data chunk),通过k个数据块计算出m个校验块(coding chunk)。把nkm个数据块保存在不同的节点,通过n中的任意k个块还原出原始数据。EC包含编码和解…

Navicat使用HTTP通道服务器进行连接mysql数据库(超简单三分钟完成),centos安装nginx和php,docker安装nginx+php合并版

序言 因为数据库服务器在外网是不能直接连接访问的,但是可以访问网站,网站后台就能访问数据库,所以在此之前,访问数据库的数据是一件非常麻烦的事情,在平时和运维的交流中发现,他们会使用ssh通道进行连接访…

【LeetCode算法系列题解】第36~40题

CONTENTS LeetCode 36. 有效的数独(中等)LeetCode 37. 解数独(困难)LeetCode 38. 外观数列(中等)LeetCode 39. 组合总和(中等) LeetCode 36. 有效的数独(中等&#xff09…

钡铼R40边缘计算网关与华为云合作,促进物联网传感器数据共享与应用

场景说明 微型气象是不可预测的,基本上不能通过人工手段来分析其变化,因此必须运用新技术,对气象进行实时监测,以便采取相应的措施来避免或解决事故的发生。而常规气象环境数据采集容易造成数据损失、人力成本高、数据安全性差、…

【数据分享】2023年7月道路数据(全国/分省/分城市/无需转发)

道路数据是我们在各项研究中经常使用的数据!道路数据虽然很常用,但是却基本没有能下载最近年份道路数据的网站,所以很多人不知道如何获到道路数据。 本次我们为大家推荐的下载道路数据的网站是Open Street Map!我们先来了解下Ope…

查漏补缺 - JS三 WebAPI

目录 BOMhistory DOM操作DOM1,dom.children 和 dom.childNodes 区别2,dom.remove()3,其他常用 API DOM 属性1,标准属性2,自定义属性 DOM 内容DOM样式DOM事件 JavaScript 包括 EcmaScript 和 WebAPI EcmaScript 包括 语…

03Linux

物联201白悦颖 学号:2008070101 青岛科技大学 目录 ⼀、进程与线程 1. 进程 2. 线程 3. 进程和线程的使用情况 ⼆、linux下的sudo命令 1. sudo 功能? sudo提供的临时权限什么时候被取消? 为什么要取消 sudo 权限…

C++之访问vector<vector<char>>中的vector<char>元素(一百八十七)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生…

ceph架构及 IO流程

CEPH是由多个节点构成的集群,它具有良好的可扩展性和可靠性。节点之间相互通信以达到: 存储和检索数据 数据复制 监控集群的健康状况 保证数据的完整性 检测故障并恢复 基本架构如下图: 分布式对象存储系统RADOS是CEPH最为关键的技术&a…

深入理解css3背景图边框

border-image知识点 重点理解 border-image-slice 设置的值将边框背景图分为9份,图像中间的舍弃,其他部分图像对应边框的相应区域放置,上右下左四角固定,border-image-repeat设置的是除四角外其他部分的显示方式。 截图来自菜鸟教…