深入理解java虚拟机精华总结:运行时栈帧结构、方法调用、字节码解释执行引擎

news/2025/1/11 15:57:27/

深入理解java虚拟机精华总结:运行时栈帧结构、方法调用、字节码解释执行引擎

  • 运行时栈帧结构
    • 局部变量表
    • 操作数栈
    • 动态连接
    • 方法返回地址
  • 方法调用
    • 解析
    • 分派
      • 静态分派
      • 动态分派
  • 基于栈的字节码解释执行引擎

运行时栈帧结构

Java虚拟机以方法作为最基本的执行单元,“栈帧”(Stack Frame) 则是用于支持虚拟机进行方法调用和方法执行背后的数据结构,它也是虚拟机运行时数据区中的虚拟机栈(Virtual Machine Stack)的栈元素,栈帧存储了方法的局部变量表操作数栈动态连接方法返回地址等信息。

在这里插入图片描述

局部变量表

局部变量表(Local Variables Table)是一组变量值的存储空间,用于存放方法参数和方法内部定义的局部变量。局部变量表的容量以变量槽(Variable Slot)为最小单位,每个变量槽都应该能存放一个boolean、byte、char、short、int、float、reference或returnAddress类型的数据。

在这里插入图片描述

操作数栈

操作数栈(Operand Stack)也常被称为操作栈,它是一个后入先出(Last In First Out,LIFO)栈

当一个方法刚刚开始执行的时候,这个方法的操作数栈是空的,在方法的执行过程中,会有各种字节码指令往操作数栈中写入和提取内容,也就是出栈和入栈操作。譬如在做算术运算的时候是通过将运算涉及的操作数栈压入栈顶后调用运算指令来进行的,又譬如在调用其他方法的时候是通过操作数栈来进行方法参数的传递。

在这里插入图片描述

动态连接

每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接(Dynamic Linking)。Class文件的常量池中存有大量的符号引用,字节码中的方法调用指令就以常量池里指向方法的符号引用作为参数。这些符号引用一部分会在类加载阶段或者第一次使用的时候就被转化为直接引用,这种转化被称为静态解析。另外一部分将在每一次运行期间都转化为直接引用,这部分就称为动态连接

在这里插入图片描述

方法返回地址

当一个方法开始执行后,只有两种方式退出这个方法。第一种方式是执行引擎遇到任意一个方法返回的字节码指令(正常调用完成);另外一种退出方式是在方法执行的过程中遇到了异常,并且这个异常没有在方法体内得到妥善处理(异常调用完成)。

在方法退出之后,必须返回到最初方法被调用时的位置。方法正常退出时,主调方法的PC计数器的值就可以作为返回地址,栈帧中很可能会保存这个计数器值。而方法异常退出时,返回地址是要通过异常处理器表来确定的,栈帧中就一般不会保存这部分信息。

退出时可能执行的操作:恢复上层方法的局部变量表和操作数栈,把返回值(如果有的话)压入调用者栈帧的操作数栈中,调整PC计数器的值以指向方法调用指令后面的一条指令等。

在这里插入图片描述

方法调用

方法调用并不等同于方法中的代码被执行,方法调用阶段唯一的任务就是确定被调用方法的版本(即调用哪一个方法),暂时还未涉及方法内部的具体运行过程。Class文件的编译过程中不包含传统程序语言编译的连接步骤,一切方法调用在Class文件里面存储的都只是符号引用,而不是方法在实际运行时内存布局中的入口地址(也就是之前说的直接引用)。

在这里插入图片描述

解析

所有方法调用的目标方法在Class文件里面都是一个常量池中的符号引用,在类加载的解析阶段,会将其中的一部分符号引用转化为直接引用,这种解析能够成立的前提是:方法在程序真正运行之前就有一个可确定的调用版本,并且这个方法的调用版本在运行期是不可改变的。这类方法的调用被称为解析(Resolution)。

只要能被invokestatic和invokespecial指令调用的方法,都可以在解析阶段中确定唯一的调用版本,Java语言里符合这个条件的方法共有静态方法、私有方法、实例构造器、父类方法4种,再加上被final修饰的方法(尽管它使用invokevirtual指令调用),这5种方法调用会在类加载的时候就可以把符号引用解析为该方法的直接引用。

在这里插入图片描述

分派

在这里插入图片描述

静态分派

所有依赖静态类型(在编译期可知)来决定方法执行版本的分派动作,都称为静态分派。静态分派的最典型应用表现就是方法重载(Overload)。静态分派发生在编译阶段,因此确定静态分派的动作实际上不是由虚拟机来执行的。

在这里插入图片描述

动态分派

动态分派的实现过程与 重写(Override) 有着很密切的关联。

正是因为invokevirtual指令执行的第一步就是在运行期确定接收者的实际类型,所以invokevirtual指令并不是把常量池中方法的符号引用解析到直接引用上就结束了,还会根据方法接收者的实际类型来选择方法版本,这个过程就是Java语言中方法重写的本质。我们把这种在运行期根据实际类型确定方法执行版本的分派过程称为动态分派。

在这里插入图片描述

基于栈的字节码解释执行引擎

一段简单的算术代码。

public int calc() {int a = 100;int b = 200;int c = 300;return (a + b) * c;
}

使用javap命令看看它的字节码指令。

public int calc();Code:Stack=2, Locals=4, Args_size=10: bipush 1002: istore_13: sipush 2006: istore_27: sipush 30010: istore_311: iload_112: iload_213: iadd14: iload_315: imul16: ireturn
}

在这里插入图片描述

首先,执行偏移地址为0的指令,Bipush指令的作用是将单字节的整型常量值(-128~127)推入操作数栈顶,跟随有一个参数,指明推送的常量值,这里是100。

在这里插入图片描述

执行偏移地址为2的指令,istore_1指令的作用是将操作数栈顶的整型值出栈并存放到第1个局部变量槽中。后续4条指令(直到偏移为11的指令为止)都是做一样的事情,也就是在对应代码中把变量a、b、c赋值为100、200、300。

在这里插入图片描述

执行偏移地址为11的指令,iload_1指令的作用是将局部变量表第1个变量槽中的整型值复制到操作数栈顶。

在这里插入图片描述

执行偏移地址为12的指令,iload_2指令的执行过程与iload_1类似,把第2个变量槽的整型值入栈。

在这里插入图片描述

执行偏移地址为13的指令,iadd指令的作用是将操作数栈中头两个栈顶元素出栈,做整型加法,然后把结果重新入栈。在iadd指令执行完毕后,栈中原有的100和200被出栈,它们的和300被重新入栈。

在这里插入图片描述
执行偏移地址为14的指令,iload_3指令把存放在第3个局部变量槽中的300入栈到操作数栈中。这时操作数栈为两个整数300。下一条指令imul是将操作数栈中头两个栈顶元素出栈,做整型乘法,然后把结果重新入栈,与iadd完全类似。

在这里插入图片描述

执行偏移地址为16的指令,ireturn指令是方法返回指令之一,它将结束方法执行并将操作数栈顶的整型值返回给该方法的调用者。到此为止,这段方法执行结束。

上面的执行过程仅仅是一种概念模型,虚拟机最终会对执行过程做出一系列优化来提高性能,实际的运作过程并不会完全符合概念模型的描述。更确切地说,实际情况会和上面描述的概念模型差距非常大,差距产生的根本原因是虚拟机中解析器和即时编译器都会对输入的字节码进行优化,即使解释器中也不是按照字节码指令去逐条执行的。


http://www.ppmy.cn/news/64502.html

相关文章

Hardhat 开发框架 - Solidity开发教程连载

Decert.me 要连载教程了, 《Solidity 开发教程》 力求系统、深入的介绍 Solidity 开发, 同时这是一套交互式教程,你可以实时的修改教程里的合约代码并运行。 本教程来自贡献者 Tiny熊,让我们正式开始学习吧。 如果你已经是 Hard…

vue2实现高德地图 JSAPI 2.0海量点标记(标注和标注图层)->自定义点位->定时刷新点位

前提: 需要注册高德开放平台,之后创建应用并且开通Web端(JS API)平台,然后拿到securityJsCode和key 1. 基础抽取(还原示例) 1.1 组件代码 代码说明: 需要修改securityJsCode和key为自己的allowCollision为标注是否避让marker,默认为false不避让markers为地图上的标记数组layer…

金融行业数据分类分级“五步走” | 盾见

文|查浩奇 《数据安全法》明确提出,国家要建立数据分类分级保护制度,根据数据在经济社会发展中的重要程度,以及一旦遭到篡改、破坏、泄露或者非法获取、非法利用,对国家安全、公共利益或者个人、组织合法权益造成的危害程度&…

运营商大数据助力贷款行业快速精准获取意向客户

流量,是企业发展的一大痛点。随着市场格局不断变化,获取流量越来越成为企业摆脱发展困局的一种重要途径,如何在庞大的市场竞争中,实现自身的流量突破,也成为企业所要解决的首要问题。 贷款行业的竞争也很强烈&#xf…

logstash介绍和使用-ELK文章2

官方 Logstash 是免费且开放的服务器端数据处理管道,能够从多个来源采集数据,转换数据,然后将数据发送到您最喜欢的“存储库”中。 下载和文档:https://www.elastic.co/cn/logstash/ docker部署:https://hub.docker.…

【9种】ElasticSearch分词器详解,一文get!!!| 博学谷狂野架构师

ElasticSearch 分词器 作者: 博学谷狂野架构师GitHub:GitHub地址 (有我精心准备的130本电子书PDF) 只分享干货、不吹水,让我们一起加油!😄 概述 分词器的主要作用将用户输入的一段文本,按照一定…

maven作用讲解---以及怎么配置阿里的maven镜像

目录 Maven介绍 传统的java项目的结构和maven的对比 传统 Maven的项目 如何配置阿里 maven 2. 修改 Maven介绍 传统的java项目的结构和maven的对比 传统 Maven的项目 如何配置阿里 maven (1) 把 D:\program\JavaIDEA 2020.2\plugins\maven\lib\maven3\conf\settings.xml…

ChatGLM-LLaMA-chinese-insturct 学习记录(含LoRA的源码理解)

ChatGLM-LLaMA-chinese-insturct 前言一、实验记录1.1 环境配置1.2 代码理解1.2.1 LoRA 1.4 实验结果 二、总结 前言 介绍:探索中文instruct数据在ChatGLM, LLaMA等LLM上微调表现,结合PEFT等方法降低资源需求。 Github: https://github.com/27182812/Ch…