栈虚拟机和寄存器虚拟机,有什么不同?

本来这节内容是打算直接讲字节码指令的,但讲之前又必须得先讲指令集架构,而指令集架构又分为两种,一种是基于栈的,一种是基于寄存器的。

那不妨我们这节就单独来讲讲栈虚拟机和寄存器虚拟机,它们有什么不同,以及各自的优缺点。

栈和寄存器

关于栈这个数据结构,我们前面曾讲过,戳链接直达。

寄存器(Register)是中央处理器(CPU)内用来暂存指令、数据和地址的存储器,也是 CPU 中读写最快的存储器。

图片来源于cxuan

图片来源于cxuan

从硬件层面来说,栈位于内存当中,而寄存器位于 CPU 当中,这也是为什么,我们通常会说,基于寄存器架构的虚拟机会比基于栈的虚拟机快的原因。

基于栈的虚拟机

前面我们讲 JDK 的发展历程时,提到了 Hotspot VM,它是血缘最正统的 Java 虚拟机。

 

HotSpot VM 是基于栈的一种虚拟机,当 Java 程序运行时,HotSpot VM 加载编译后的字节码文件(也就是.class 文件),其解释器或JIT编译器会读取文件中的字节码指令,将它们解释(或编译)为机器码。

方法调用和执行过程中的数据(如局部变量和中间结果)会存储在栈(操作数栈,下面会讲)中,字节码指令操作这些数据,然后执行程序逻辑。

下面这幅图我们之前在讲JVM 是如何运行 Java 代码的时候讲过。

 

main 方法被执行的时候,JVM 会创建一个栈帧(Stack Frame),通过存储局部变量表、操作数栈、动态链接、方法出口等信息来支撑和完成方法的执行,栈帧就是虚拟机栈中的子单位。

 

栈帧本身也是一种栈结构,用于支持虚拟机进行方法调用和方法执行,遵循 LIFO 的原则,每个栈帧都包含了一个方法的运行信息,每个方法从调用到执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈和出栈的过程。

图片来源于网络,作者浣熊say

图片来源于网络,作者浣熊say

虚拟机栈是线程私有的,每个线程都有自己的 Java 虚拟机栈。方法调用时都会创建一个新的栈帧,该栈帧被推入虚拟机栈,成为当前活动栈帧。

  • 入栈:方法调用时,虚拟机栈会为这个方法分配一个栈帧,这个栈帧被压入虚拟机栈,成为当前的活动栈帧。PC 寄存器指向当前栈帧的指令,执行方法的指令序列从该地址开始。
  • 出栈:方法执行完成后,对应的栈帧会被移除,控制权回到前一个栈帧,前一个栈帧中的返回值成为当前活动栈帧的一个操作数,继续执行。

其中的操作数栈(Operand Stack)也是一种栈结构,用于保存方法执行时的中间结果、参数和返回值。当一个方法刚刚开始执行的时候,这个方法的操作数栈是空的。

在方法执行的过程中,操作数栈被用于执行各种字节码指令。例如,将两个数字相加的指令会从操作数栈中弹出两个数字,将它们相加,然后将结果压入操作数栈中。

另外,操作数栈的内容是临时的,它的生命周期和方法的生命周期是一样的,当方法执行结束后,操作数栈也会被销毁。

 

R 大曾在知乎的贴子里提到过:

VM 当初设计的时候非常重视代码传输和存储的开销,因为假定的应用场景是诸如手持设备、机顶盒之类的嵌入式应用,所以要代码尽量小;外加基于栈的实现更简单(无论是在源码编译器的一侧还是在虚拟机的一侧),而且主要设计者 James Gosling 的个人经验上也对这种做法非常熟悉(例如他之前实现过 PostScript 的虚拟机,也是基于栈的指令集),所以就选择了基于栈。

我们简单来看一下基于栈的虚拟机方法执行的过程,以下面的代码为例:

int a = 33;
int b = 44;
int c = a + b;

通过 javap -c Main 命令可以查看对应的字节码,如下所示:

Compiled from "Main.java"
public class com.github.paicoding.forum.test.javabetter.jvm.Main {
  public static void main(java.lang.String[]);
    Code:
       0: bipush        33
       2: istore_1
       3: bipush        44
       5: istore_2
       6: iload_1
       7: iload_2
       8: iadd
       9: istore_3
      10: return
}

我们用图来说明指令执行的过程,大致如下。

 

  • iload_0 将 33 压入操作数栈中
  • iload_1 将 44 压入操作数栈中
  • iadd 将操作数栈中的 33 和 44 弹出,相加后将结果 77 压入操作数栈中
  • istore_2 将栈顶的 77 弹出,存入局部变量表中下标为 2 的位置

关于字节码指令的具体释义,我们放到下一节去细讲,这里主要是带大家体会一下基于栈的虚拟机和基于寄存器的虚拟机之间的差别。

基于寄存器的虚拟机

那除了有基于栈的虚拟机实现,当然也有基于寄存器的虚拟机实现,比如 LuaVM,负责执行 Lua 语言,一门轻量级的脚本语言,可戳链接了解。

5.0 之前的 Lua 其实是用基于栈的指令集,到 5.0 才改为用基于寄存器的。出于两点考虑,一是减少数据移动次数,降低数据迁移带来的拷贝开销;二是减少虚拟指令条数,提高指令执行效率。

 

好,我们就基于 lua 来看一下基于寄存器的虚拟机方法执行的过程。

第一步,安装 lua,这里我用的是 macOS,直接用 brew 安装就好了。

brew install lua

Windows 用户可以查看这个文档:lua-users wiki: Building Lua In Windows For Newbies

也可以通过 Lua for Windows 来完成安装:

Releases · rjpcomputing/luaforwindows · GitHub

我们来编写一段简单的 lua 代码,保存为 example.lua。

local a = 33
local b = 44
local c = a + b

然后查看字节码指令。

luac -l example.lua

结果如下:

 

main <example.lua:0,0> (6 instructions at 0x600002144080)
0+ params, 3 slots, 1 upvalue, 3 locals, 0 constants, 0 functions

这是函数的描述,表示这是 example.lua 文件中的主函数。它包含 6 条指令。函数不接受参数(0+ params),有 3 个本地变量槽位(3 slots),1 个闭包变量(1 upvalue),3 个本地变量(3 locals),没有常量(0 constants)和内部函数(0 functions)。

接下来是具体的指令:

  1. VARARGPREP 0:准备变长参数,用于处理传入的参数。
  2. LOADI 0 33:将整数 33 加载到寄存器 0。
  3. LOADI 1 44:将整数 44 加载到寄存器 1。
  4. ADD 2 0 1:将寄存器 0 和寄存器 1 中的值相加,并将结果存放在寄存器 2。对应于脚本中两个数值的加法操作。
  5. MMBIN 0 1 6; add:这是一个元方法(metamethod)调用,用于处理加法操作。这指示 Lua 虚拟机查找并执行 add 元方法。元方法是 Lua 中用于重载标准操作符的特殊方法。
  6. RETURN 3 1 1; 0 out:返回操作,将寄存器 3 中的值作为返回值。1 1 表示从寄存器 3 返回一个值,0 out 指没有额外的返回值。

小结

基于栈的优点是可移植性更好、指令更短、实现起来简单,但不能随机访问栈中的元素,完成相同功能所需要的指令数也比寄存器的要多,需要频繁的入栈和出栈。

基于寄存器的优点是速度快,有利于程序运行速度的优化,但操作数需要显式指定,指令也比较长。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Vision - 开源视觉分割算法框架 Grounded SAM2 配置与推理 教程 (1)

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/143388189 免责声明&#xff1a;本文来源于个人知识与公开资料&#xff0c;仅用于学术交流&#xff0c;欢迎讨论&#xff0c;不支持转载。 Ground…

vxe-table v4.8+ 与 v3.10+ 虚拟滚动支持动态行高,虚拟渲染更快了

Vxe UI vue vxe-table v4.8 与 v3.10 解决了老版本虚拟滚动不支持动态行高的问题&#xff0c;重构了虚拟渲染&#xff0c;渲染性能大幅提升了&#xff0c;行高自适应和列宽拖动都支持&#xff0c;大幅降低虚拟渲染过程中的滚动白屏&#xff0c;大量数据列表滚动更加流畅。 自适…

Docker | 将本地项目发布到阿里云的实现流程

发布到阿里云 本地镜像发布到阿里云流程具体流程1. docker commit 生成新镜像文件2. 查看镜像3. 阿里云开发者平台选择控制台&#xff0c;进入容器镜像服务&#xff0c;选择个人实例创建命名空间仓库名称进入管理界面获得脚本推送到阿里云 补充&#xff1a; docker tag 命令基本…

龙迅#LT8668EX显示器图像处理芯片 适用于HDMI1.4+VGA转4PORT LVDS,支持4K30HZ分辨率,可做OSD菜单亮度调节!

1. 一般说明 LT8668EX 是 Lontium 的第二代 LCD 控制器&#xff0c;基于 ClearEdge 技术&#xff0c;支持 VGA 接口和 HDMI 接口&#xff0c;符合 HDMI 1.4 规范。它可以支持带 HDMI 接口的双模 DP。为了向后兼容&#xff0c;该 LCD 控制器还包括一个高性能模拟接口&#xff0…

如何在Linux系统中使用Apache HTTP Server

如何在Linux系统中使用Apache HTTP Server Apache简介 安装Apache 在Debian/Ubuntu系统中安装 在CentOS/RHEL系统中安装 启动Apache服务 验证Apache是否正在运行 访问Apache默认页面 配置Apache虚拟主机 创建虚拟主机配置文件 示例虚拟主机配置 创建网站根目录 准备静态网站内…

【大模型开发指南】llamaindex配置deepseek、jina embedding及chromadb实现本地RAG及知识库(win系统、CPU适配)

说一些坑&#xff0c;本来之前准备用milvus&#xff0c;但是发现win搞不了&#xff08;docker都配好了&#xff09;。然后转头搞chromadb。这里面还有就是embedding一般都是本地部署&#xff0c;但我电脑是cpu的没法玩&#xff0c;我就选了jina的embedding性能较优&#xff08;…

拔得头筹 | 怿星科技斩获第四届“乌镇杯”高层次人才创业创新大赛一等奖

10月31日&#xff0c;第四届“乌镇杯”高层次人才创业创新大赛总决赛在桐乡市隆重举行&#xff0c;怿星科技“智能汽车软件研发测试工具链”项目成功入围总决赛&#xff0c;并荣获“人工智能”赛道一等奖。 本次大赛以“e城桐创智领未来”为主题&#xff0c;围绕桐乡市重点产业…

鸢尾博客项目总结

1.博客介绍 鸢尾博客是一个基于Spring BootVue3 TypeScript ViteJavaFx的客户端和服务器端的博客系统。项目采用前端与后端分离&#xff0c;支持移动端自适应&#xff0c;配有完备的前台和后台管理功能。后端使用Sa-Token进行权限管理,支持动态菜单权限&#xff0c;服务健康…

Installshield 总是跳出 Activation 激活对话框,而且创建项目失败

今天打开InstallShield &#xff0c;总是出现这个对话框&#xff0c;而且输入序列号后&#xff0c;虽然现实激活&#xff0c;但是无论打开原来的项目&#xff0c;还是新建项目都是失败。 解决方法&#xff1a; 一直没有思路&#xff0c;后来&#xff0c;使用管理员打开VS&#…

TLV320AIC3104IRHBR 数据手册 一款低功耗立体声音频编解码器 立体声耳机放大器芯片麦克风

TLV320AIC3104 是一款低功耗立体声音频编解码器&#xff0c;具有立体声耳机放大器以及在单端或全差分配置下可编程的多个输入和输出。该器件包括基于寄存器的全面电源控制&#xff0c;可实现立体声 48kHz DAC 回放&#xff0c;在 3.3V 模拟电源电压下的功耗低至 14mW&#xff0…

11月第一篇新作,十一月对我好一点:C++之继承(2)

C之继承&#xff08;2&#xff09; 虚继承 很多⼈说C语法复杂&#xff0c;其实多继承就是⼀个体现。有了多继承&#xff0c;就存在菱形继承&#xff0c;有了菱形继承就有 菱形虚拟继承&#xff0c;底层实现就很复杂&#xff0c;性能也会有⼀些损失&#xff0c;所以最好不要设计…

uni-app 封装图表功能

文章目录 需求分析1. 秋云 uchars2. Echarts 需求 在 uni-app 中使用图表功能&#xff0c;两种推荐的图表工具 分析 在 Dcloud市场 搜索Echarts关键词&#xff0c;会出现几款图表工具&#xff0c;通过大家的下载量&#xff0c;可以看到秋云这个库是比较受欢迎的&#xff0c;其…

FemtoMega的开发者模式教程

1.FemtoMega相机简介 Femto Mega作为奥比中光联合微软、英伟达共同推出的全新升级iToF相机&#xff0c;整合英伟达算力及微软深度引擎技术&#xff0c;整机具备高通用性优势&#xff0c;集成深度算力无需额外算力&#xff0c;此外还支持POE网络接口&#xff0c;支持远程的部署和…

基于SpringBoot的健身房系统的设计与实现(源码+定制+开发)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

索引的使用以及使用索引优化sql

索引就是一种快速查询和检索数据的数据结构&#xff0c;mysql中的索引结构有&#xff1a;B树和Hash。 索引的作用就相当于目录的作用&#xff0c;我么只需先去目录里面查找字的位置&#xff0c;然后回家诶翻到那一页就行了&#xff0c;这样查找非常快&#xff0c; 一、索引的使…

Dockerfile 增强新语法

Dockerfile 是使用 Docker 的相关开发人员的基本工具&#xff0c;用来充当构建 Docker 镜像的模板&#xff0c;在这个文件中包含用户可以在命令行上调用来构建镜像的所有命令。了解并有效利用 Dockerfile 可以显着简化开发流程&#xff0c;实现镜像创建的自动化并确保不同开发阶…

【搜索引擎】俄罗斯搜索引擎yandex

俄罗斯搜索引擎yandex 1997年&#xff0c;俄罗斯搜索引擎Yandex&#xff08;俄语意为&#xff1a;语言目录&#xff09;首次上线&#xff0c;已发展成为全球第四大搜索引擎和第二大非英语搜索引擎 https://yandex.com/

Vue v-on

vue : v-on:func --------------------------- data(){ return{ prop:any; } } methods:{ func(){ } } template:, v-on

【django】django RESTFramework前后端分离框架快速入门

目录 一、搭建项目开发环境 1.1 pycharm创建项目 1.2 修改配置settings.py 1.3 新增 static与staticfiles文件夹 1.4 生成数据表 1.5 创建超级用户 1.6 启动项目 二、安装REST_Framework 2.1 安装 2.2 配置settings 2.3 重新执行生成数据库脚本 三、修改路由 四、s…

基于centos7.9搭建MariaDB10.5高可用集群

MariaDB-HA 环境初始化安装MariaDB配置集群 基于centos7.9搭建MariaDB10.5数据库高可用集群&#xff0c;对标mysql5.7 节点IPnode1192.168.200.101node2192.168.200.102node3192.168.200.103 环境初始化 #!/bin/bash# 定义节点信息 NODES("192.168.200.101 node1"…