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

虚拟机字节码执行引擎

文章目录

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


一、概述

执行引擎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.mzph.cn/news/68455.shtml

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

相关文章

【网络安全带你练爬虫-100练】第20练:数据处理-并写入到指定文档位置

目录 一、目标1:解码去标签 二、目标2:提取标签内内容 三、目标3:处理后的数据插入原位置 四、目标4:将指定的内容插入指定的位置 五、目标5:设置上下文字体格式 六、目标6:向多个不同位置插入不同的…

企业如何充分借助大数据下精准营销?

技术的发展和智能终端的普及移动互联网用户的大规模增长使移动互联网快速发展,使中国移动互联网软件进入移动互联网时代越来越多地涉及到改变生活大家习惯。移动互联网时代的到来也意味着大数据时代的到来。精准营销数据方法,移动互联网和大数据的兴起不…

ZLMediaKit 重建docker包

1.下载容器到本地服务器并运行 #此镜像为github持续集成自动编译推送,跟代码(master分支)保持最新状态 docker run -id -p 1935:1935 -p 8080:80 -p 8443:443 -p 8554:554 -p 10000:10000 -p 10000:10000/udp -p 8000:8000/udp -p 9000:9000/udp zlmediakit/zlmedi…

App自动化测试持续集成效率提高50%

持续集成是一种开发实践,它倡导团队成员需要频繁的集成他们的工作,每次集成都通过自动化构建(包括编译、构建、自动化测试)来验证,从而尽快地发现集成中的错误。让正在开发的软件始终处于可工作状态,让产品…

拦截器和异常处理器

拦截器和异常处理器 拦截器 拦截器(Interceptor)&#xff0c;主要完成请求参数的解析、将页面表单参数赋给值栈中相应属性、执行功能检验、程序异常调试等工作。 准备 创建模块 如下为完整的项目结构 web.xml <web-app xmlns"http://xmlns.jcp.org/xml/ns/javaee&qu…

macos 不支持svn安装

macos 10.13可能不支持svn命令,所以要安装 xcode-select --install 弹窗在线安装失败的话只能手动下载安装 打开:Sign In - Apple 搜索Command Line Tools (macOS 10.13) 下载9.4.1版本直接安装后即可

利用vba处理Excel表格数据实现键值转化,适用于将编码转化成对应的文本

最近遇到了一个甲方需要提供系统登录的用户名单和对应的角色权限内容。无奈直接从数据库导出的数据对应的都是编码&#xff0c;没有转成中文&#xff0c;想着偷个懒能不能直接用Excel直接转&#xff0c;网上看了一下有修改单元格格式的&#xff0c;但需要编码是2到3个。多的就用…

TCP机制之连接管理(三次握手和四次挥手详解)

TCP的连接管理机制描述了连接如何创建以及如何断开! 建立连接(三次握手) 三次握手的过程 所谓建立连接就是通信双方各自要记录对方的信息,彼此之间要相互认同;这里以A B双方确立男女朋友关系为例: 从图中可以看出,通信双方各自向对方发起一个"建立连接"的请求,同时…

RK3568-i2c-适配8010rtc时钟芯片

硬件连接 从硬件原理图中可以看出&#xff0c;rtc时钟芯片挂载在i2c3总线上&#xff0c;设备地址需要查看芯片数据手册。编写设备树 &i2c3 {status "okay";rx8010: rx801032 {compatible "epson,rx8010";reg <0x32>;}; };使能驱动 /kernel/…

Redis布隆过滤器原理

其实布隆过滤器本质上要解决的问题&#xff0c;就是防止很多没有意义的、恶意的请求穿透Redis&#xff08;因为Redis中没有数据&#xff09;直接打入到DB。它是Redis中的一个modules&#xff0c;其实可以理解为一个插件&#xff0c;用来拓展实现额外的功能。 可以简单理解布隆…

【闭源期刊】Elsevier旗下,1区(TOP),3个月录用

闭源期刊 1区&#xff08;TOP&#xff09; 出版社&#xff1a;Elsevier 影响因子&#xff1a;IF&#xff08;2022&#xff09;8.5-9.0 期刊分区&#xff1a;JCR1区&#xff0c;中科院2区&#xff08;无预警记录&#xff09; 检索情况&#xff1a;SCIE&EI 双检&#xff…

slog正式版来了:Go日志记录新选择!

在大约一年前&#xff0c;我就写下了《slog&#xff1a;Go官方版结构化日志包[1]》一文&#xff0c;文中介绍了Go团队正在设计并计划在下一个Go版本中落地的Go官方结构化日志包&#xff1a;slog[2]。但slog并未如预期在Go 1.20版本[3]中落地&#xff0c;而是在golang.org/x/exp…

Vulnhub: Hogwarts: Bellatrix靶机

kali&#xff1a;192.168.111.111 靶机&#xff1a;192.168.111.228 信息收集 端口扫描 nmap -A -sC -v -sV -T5 -p- --scripthttp-enum 192.168.111.228访问80端口 查看源码&#xff0c;提示ikilledsiriusblack.php和文件包含的参数名file 漏洞利用 ikilledsiriusblack.p…

【Eclipse】搭建python环境;运行第一个python程序helloword

目录 0.环境 1.需准备&搭建思路 2.搭建具体步骤 1&#xff09;查看是否安装过python 2&#xff09;安装eclipse 3&#xff09;安装和配置pyDev 3.创建第一个python程序具体步骤 1&#xff09;新建项目 2&#xff09;输入项目名字&#xff0c;和配置选项 3&#x…

elasticsearch的搜索补全提示

当用户在搜索框输入字符时&#xff0c;我们应该提示出与该字符有关的搜索项 拼音分词器 下载 要实现根据字母做补全&#xff0c;就必须对文档按照拼音分词&#xff0c;GitHub上有拼音分词插件 GitHub - medcl/elasticsearch-analysis-pinyin: This Pinyin Analysis plugin…

【Linux】地址空间概念

目录 前言&#xff1a; 地址空间回顾 验证&#xff1a;一个变量是否会有两个值&#xff1f; 一. 什么是地址空间 虚拟地址与物理地址之间的关系 二. 地址空间是如何设计的 1. 回答一个变量两个值 2.扩展 继续深入理解 三. 为什么要有地址空间 原因&#xff1a; 1. 使…

基于SSM的新能源汽车在线租赁系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用Vue技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

LNMP 平台搭建(四十)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 搭建LNMP 一、安装Nginx 二、安装Mysql 三、安装PHP 四、部署应用 前言 LNMP平台指的是将Linux、Nginx、MySQL和PHP&#xff08;或者其他的编程语言&#xff0c;如…

【人月神话】深入了解软件工程和项目管理

文章目录 &#x1f468;‍⚖️《人月神话》的主要观点&#x1f468;‍&#x1f3eb;《人月神话》的主要内容&#x1f468;‍&#x1f4bb;作者介绍 &#x1f338;&#x1f338;&#x1f338;&#x1f337;&#x1f337;&#x1f337;&#x1f490;&#x1f490;&#x1f490;&a…

华为数通安全产品介绍

HiSecEngine USG12000系列防火墙&#xff08;以下简称USG12000系列&#xff09;是华为公司推出的首款T级AI防火墙&#xff0c;在网络边界实时防护已知与未知威胁&#xff0c;通常部署在云计算数据中心&#xff0c;大型企业及园区网出口&#xff0c;为数据中心、企业及园区网络提…