基于栈的运行方式

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. TVM Pass优化 -- InferType 类型推导

    定义(What) InferType,类型推断,顾名思义,给表达式进行类型的推断 直接上代码 import tvm from tvm import relay import numpy as np d ...

  2. 第9.2讲、Tiny Decoder(带 Mask)详解与实战

    自己搭建一个 Tiny Decoder(带 Mask),参考 Transformer Encoder 的结构,并添加 Masked Multi-Head Self-Attention,它是 Decod ...

  3. 使用SimpleDateFormat获取指定时区时间

    摘要:使用SimpleDateFormat把时间戳转换成指定格式的.指定时区的字符串.   SimpleDateFormat是Java中的一个日期格式化类,继承了DateFormat,可以实现日期时间 ...

  4. Spring中的一些注解

    @SuppressWarnings("all") 隐藏一些无意义的报错提示 ** @ConfigurationProperties(prefix = "spring.da ...

  5. 开源大数据调度系统Taier技术公开课——Taier数据开发介绍

    一键直达直播间 一.直播介绍 之前的内容,我们为大家分享了Taier入门及控制台,以及Taier的Web前端架构介绍,本期我们为大家分享Taier数据开发介绍. 本次直播我们将从Taier的数据开发功 ...

  6. LinqPad:C#代码测试学习一品神器

    01.LinqPad是干嘛的? LinqPad 是一个可编写.运行C#(VB/F#/SQL)代码的轻量工具,C#开发者强烈推荐必备的工具,一个一旦用了就再也不想丢弃的工具.工具出自<C# 核心技 ...

  7. Js 时间类型转换

    Date.prototype.format = function (format) { var args = { "M+": this.getMonth() + 1, " ...

  8. CF1227G Not Same 题解

    CF1227G Not Same 构造.考虑按照每个数字进行考虑,每次填充一列. 观察样例 \(1\),不难发现可以构造使每一行或列一定有一个位置为 \(0\).我们不妨对于每一列限定这个 \(0\) ...

  9. 前端开发系列069-JQuery篇之框架动画特效

    一.jQuery动画特效说明 jQuery框架中为我们封装了众多的动画和特效方法,只需要调用对应的动画方法传递合适的参数,就能够方便的实现一些炫酷的效果,而且jQuery框架还支持自定义各种动画效果. ...

  10. 在服务器上安装 gitlab

    简介 RT 核心参考链接 https://about.gitlab.com/install/#ubuntu?version=ce https://www.cnblogs.com/zhujingzhi/ ...