基于栈的运行方式

Java虚拟机的执行过程基于字节码指令,可以将其视为对操作系统的一种抽象模拟。Java虚拟机具有自己的指令集和运行环境,包括堆(Heap)、栈(Stack)、方法区(Method Area)等。因此,Java虚拟机的指令操作流程与处理器的指令操作流程有许多相似之处,主要包括取指令、解码指令、执行指令以及更新计算等步骤。

Java虚拟机的指令集架构(Instruction Set Architecture,ISA)主要有两种实现方式:基于栈和基于寄存器。基于寄存器的指令集由于指令直接在寄存器中执行,因此执行效率较高。然而,由于寄存器是由硬件直接提供的,因此其数量和性能受到硬件的限制。

相比之下,基于栈的指令集的优点在于其高度的可移植性和相对容易的实现方式。寄存器的分配和管理是基于寄存器的指令集实现的一个主要难点。然而,基于栈的指令集的缺点是其执行速度相对较慢,这主要有两个原因:

1)基于栈的指令集需要维护出栈和入栈操作,这需要更多的指令,从而间接降低了执行效率。

2)基于栈的指令集的操作是对内存的访问,相比于处理器,内存的访问速度较慢。以下代码为例,来看两种指令集架构的差异:

int a = 1
int b = 2
int c = a + b

编译出来基于寄存器的指令:

add ax bx // AX寄存器的值为1,BX寄存器的值为2,将结果放入AX

编译出来基于栈的指令:

1 iload_0	// 操作数栈读取局部变量的第1个slot
2 iload_1 // 操作数栈读取局部变量的第2个slot
3 iadd // 将栈顶的两个slot相加
4 istore_2 // 保存到局部变量中第3个slot

栈架构的指令运算时是与操作数栈交互,即使是数据传递这样的简单操作。这样的好处就是可以忽视具体的物理架构。默认操作数存放在操作数栈上,运算后结果存放在栈顶,指令无需显式指定操作数的来源和去向。因此栈架构指令集的代码紧凑,一般都是一个或者两个字节,但所需的指令数量会比寄存器架构多。

栈的运行时

在Java虚拟机中,栈帧(Stack Frame)是一个关键的数据结构,用于支持方法的执行和方法之间的调用。每个栈帧由多个重要部分构成,包括局部变量表(Local Variable Table)、操作数栈(Operand Stack)、动态链接信息(Dynamic Linking)和方法返回地址(Return Address)。

局部变量表用于存储方法的参数以及方法内定义的局部变量。操作数栈则用于保存临时数据,特别是在执行算术运算或表达式时的中间计算结果。动态链接信息用于在方法调用过程中进行动态链接,这一机制是Java实现多态性(Polymorphism)的重要保障。方法返回地址则记录了方法执行完毕后,程序需要返回到的代码位置。

当方法被调用时,Java虚拟机会为该方法分配一个新的栈帧,并将其推入当前线程的栈顶。在方法执行过程中,虚拟机会根据字节码指令对操作数栈和局部变量表进行一系列操作,包括数据加载、存储、算术运算以及类型转换等。

当方法执行完成后,无论是正常退出还是由于未捕获的异常终止,Java虚拟机会将当前栈帧弹出,并将控制权转交给上一个栈帧,具体来说,是转交给方法返回地址所指定的位置。这个过程会持续进行,直到所有的栈帧都被弹出,标志着Java程序的执行结束。

操作数栈(Operand Stack),也被称为表达式栈(Expression Stack),是Java虚拟机执行计算的核心工作区域。它的深度是在编译期间通过代码分析计算出来的,并记录在方法的Code属性中。

操作数栈主要负责存储指令执行过程中的中间结果。几乎所有的字节码指令都会与操作数栈进行交互。例如,iadd 指令会从操作数栈顶弹出两个整数,相加后将结果压回操作数栈。这些中间结果可以是各种Java数据类型,包括基本类型(如 int, float, long, double)和对象引用(reference)。

此外,操作数栈还承担着在方法调用和返回过程中参数和返回值的传递任务。当方法被调用时,调用者方法计算出传递给被调用方法的参数值,并将这些参数值依次压入调用者自身的操作数栈。方法调用指令(如 invokevirtual, invokestatic)会消耗这些参数值,并将它们传递给被调用方法。在被调用方法的新栈帧中,这些参数值通常会从调用者的操作数栈转移到被调用方法栈帧的局部变量表中;当方法执行完毕并返回时,被调用方法将其计算得到的返回值(如果有)压入其自身的操作数栈顶。返回指令(如 ireturn, areturn)会将被调用方法栈帧的这个返回值弹出,并压入调用者方法的操作数栈顶,供调用者后续使用。

以下是一个简单的Java方法,以及对应的字节码指令,展示了操作数栈的使用:

public int add(int a, int b) {
return a + b;
}

对应的字节码指令(使用javap -c命令查看):

public int add(int, int);
Code:
0: iload_1 // 将局部变量表索引1处的值(即参数a)压入操作数栈
1: iload_2 // 将局部变量表索引2处的值(即参数b)压入操作数栈
2: iadd // 从操作数栈弹出两个int值,相加后将结果压入操作数栈
3: ireturn // 从操作数栈弹出顶部int值,作为方法的返回值

在上述字节码指令中,iload_1和iload_2指令将局部变量表中的值压入操作数栈,iadd指令从操作数栈弹出两个值进行相加操作,并将结果压回操作数栈,最后ireturn指令从操作数栈弹出顶部值作为方法的返回值。

未完待续

很高兴与你相遇!如果你喜欢本文内容,记得关注哦!!!

a+b=c,处理器一步搞定,Java虚拟机为啥要四步?的更多相关文章

  1. 【开源】简单4步搞定QQ登录,无需什么代码功底【无语言界限】

    说17号发超简单的教程就17号,qq核审通过后就封装了这个,现在放出来~~ 这个是我封装的一个开源项目:https://github.com/dunitian/LoTQQLogin ————————— ...

  2. APP设计师拿到APP产品原型开始,七步搞定APP设计(转)

    任何一款成功的APP都需要以坚实的产品概念作为基础,因为概念决定了产品最终完成的潜力. 一般情况下,交到app设计师手里的都是移动app产品原型图.当然这个是在移动产品经理反复斟酌,并且与大家开会讨论 ...

  3. iOS开发三步搞定百度推送

    iOS开发三步搞定百度推送   百度推送很简单,准备工作:在百度云推送平台注册应用,上传证书. 步骤一: 百度云推送平台 http://push.baidu.com/sdk/push_client_s ...

  4. SmaterWeatherApi---签名加密和数据訪问--简单粗暴一步搞定

    -----------------------------------------------------更新-2014-07-09---------------------------------- ...

  5. 破解jar包5步搞定,jira7.9.2操作成功,附github代码库

    1,从要破解的程序中拷贝.jar包文件,运行1_jar.sh将其解压.以jira7.9.2为例: $install_dir\JIRA\atlassian-jira\WEB-INF\lib\atlass ...

  6. 轻松八步搞定Cacti配置安装(原创视频)

    轻松八步搞定Cacti配置安装 1.安装web server $sudo apt-get install apache2 验证 http://localhost 2.$sudo apt-get ins ...

  7. Docker 一步搞定 ZooKeeper 集群的搭建

    Docker 一步搞定 ZooKeeper 集群的搭建 背景 原来学习 ZK 时, 我是在本地搭建的伪集群, 虽然说使用起来没有什么问题, 但是总感觉部署起来有点麻烦. 刚好我发现了 ZK 已经有了 ...

  8. Java性能调优攻略全分享,5步搞定!(附超全技能图谱)

    对于很多研发人员来说,Java 性能调优都是很头疼的问题,为什么这么说?如今,一个简单的系统就囊括了应用程序.数据库.容器.操作系统.网络等技术,线上一旦出现性能问题,就可能要你协调多方面组件去进行优 ...

  9. 五步搞定Android开发环境部署

    引言   在windows安装Android的开发环境不简单也说不上算复杂,本文写给第一次想在自己Windows上建立Android开发环境投入 Android浪潮的朋友们,为了确保大家能顺利完成开发 ...

  10. 五步搞定Android开发环境部署——非常详细的Android开发环境搭建教程

      在windows安装Android的开发环境不简单也说不上算复杂,本文写给第一次想在自己Windows上建立Android开发环境投入Android浪潮的朋友们,为了确保大家能顺利完成开发环境的搭 ...

随机推荐

  1. 推荐一个Elasticsearch ES可视化客户端工具:ES-King

    ES-King:开源免费,一个现代.实用的ES GUI客户端,支持多平台. 下载地址:https://github.com/Bronya0/ES-King 功能清单 详尽的集群信息:节点信息.堆内存占 ...

  2. CVE-2021-41773 && CVE-2021-42013拆解复现

    CVE-2021-41773 && CVE-2021-42013 参考了这个师傅的WP https://www.jianshu.com/p/3076d9ec68cf CVE-2021- ...

  3. 在java中使用lua脚本操作redis

    前言 众所周知,redis可以执行lua脚本,至于为什么要用lua脚本来操作redis,自行百度咯 开始 Bean类 package cn.daenx.myadmin.common.config.re ...

  4. CSP-S 2020模拟训练题1-信友队T2 挑战NPC

    题意简述 有一个\(k\)维空间,每维的跨度为\(L\),即每一维的坐标只能是\(0,1, \cdots ,L-1\).每一步你可以移动到任意一个曼哈顿距离到自己小于等于\(d\)的任意一个合法坐标. ...

  5. 二叉排序树BST及CRUD操作

    摘要 构造一颗二叉排序树(也叫二叉搜索树,BST,Binary Search Tree)十分简单.一般来讲,大于根节点的放在根节点的右子树上,小于根节点的放在根节点的左子树上(如果等于根节点,则可视情 ...

  6. onnxruntime-gpu创建会话时报错:Could not load symbol cudnnGetLibConfig. Error code 127

      最近在新的设备上做模型的demo,模型训练好之后,我习惯的拉取了我基于pyqt5和onnxruntime的demo演示代码,我按照我的cuda和cudnn环境,安装了1.19的onnxruntim ...

  7. k8s在线修改alertmanager.yaml

    简单说明 有些情况下,我们可以进入集群查看某些secret的配置,这里如果想修改secret的配置时,没有secrets的源文件,可以采取如下的方案. 例如我们需要修改alertmanager.yam ...

  8. 基于CentOS Stream 8的物联网平台深度优化方案

    系统架构全景图 图表 一.系统平台优化(CentOS Stream 8) 1. 系统基础配置 bash # 1. 系统更新与加固 sudo dnf update -y sudo dnf install ...

  9. DAG任务调度系统 Taier 演进之道,探究DataSourceX 模块

    熟悉Taier的小伙伴们应该都知道,在11月7日发布的Taier1.3新版本中,我们融合了「DataSourceX 模块」.这是十分重要的一个变化,移除Taier外部插件依赖,新增数据源插件相关特性, ...

  10. SQL Server 事务中返回错误消息

    RAISERROR ('错误消息!', 16, 1)            ROLLBACK TRANSACTION            RETURN -6000;