JVM入门到入土-Java虚拟机寄存器指令集与栈指令集
JVM入门到入土-Java虚拟机寄存器指令集与栈指令集
HotSpot虚拟机中的任何操作都需要入栈和出栈的步骤。
由于跨平台性的设计,Java的指令都是根据栈来设计的。不同平台CPU架构不同,所以不能设计为基于寄存器的。优点是跨平台,指令集小,编译器容易实现,缺点是性能下降,实现同样的功能需要更多的指令。
参考资料
- Java虚拟机规范(JavaSE8)
- 深入理解Java虚拟机
JVM的两大指令集特点
基于栈式架构的特点
设计和实现更简单,适用于资源受限的系统(HotSpot虚拟机就基于此):
- 避开了寄存器的分配难题: 使用零地址指令方式分配。
- 指令流中的指令大部分是零地址指令,其执行过程依赖于操作栈。
- 指令集更小编译器容易实现。
- 不需要硬件支持,可移植性更好,更好实现跨平台
基于寄存器架构的特点
典型的应用是x86的二进制指令集: 比如传统的PC以及Android的Davlik虚拟机。
- 指令集架构则完全依赖硬件,可移植性差
- 性能优秀和执行更高效,花费更少的指令去完成一项操作。
- 在大部分情况下,基于寄存器架构的指令集往往都以一地址指令、二地址指令和三地址指令为主,而基于栈式架构的指令集却是以零地址指令为主。
获取栈指令集-指令javap
注意:如果你使用JDK17,可能会出现找不到控制台指令的问题(因为默认安装不会配置该指令),去JDK17的安装目录将bin添加到环境变量即可
先编译一个简单的1+2程序:
public class StackOneTest {public static void main(String[] args) {int a = 1;int b = 2;int c = a+b;}
}
然后使用javap -c class文件全名,我们将在控制台得到如下内容:
D:\CodeProjects\Eclipse\StackTest\bin\testjava>javap -c StackOneTest.class
Compiled from "StackOneTest.java"
public class testjava.StackOneTest {public testjava.StackOneTest();Code:0: aload_01: invokespecial #8 // Method java/lang/Object."<init>":()V4: returnpublic static void main(java.lang.String[]);Code:0: iconst_11: istore_12: iconst_23: istore_24: iload_15: iload_26: iadd7: istore_38: return
}
由于手里没有基于寄存器的指令集实验环境,可自行查阅查看方法
栈指令集字节码的语义
字节码指令集设计概述
在 Java 虚拟机的指令集中,大多数的指令都包含了其所操作的数据类型信息。例如,
iload指令用于从局部变量表中加载int类型的数据到操作数栈中,而ad指加载的则是float类型的数据。这两个指令的操作可能会是由同一段代码来实现的,但它们必须拥有各自独立的操作码。对于大部分与数据类型相关的字节码指令来说,它们的操作码助记符中都有特殊的字符来表明该指令为哪种数据类型服务:i代表对int型的数据操作,l代表long,s代表short,b代表byte,c代表char,f代表float,d代表double,a代表reference。也有一些指令的助记符没有明确用字母指明数据类型,例如arraylength指令,它没有代表数据类型的特殊字符,但操作数永远只能是一个数组类型的对象。还有另外一些指令,例如,无条件跳转指令goto则是与数据类型无关的。因为 Java 虚拟机的操作码长度只有一个字节,所以包含了数据类型的操作码给指令集的设计带来了很大的压力。如果每一种与数据类型相关的指令都支持 Java 虚拟机的所有运行时数据类型,那恐怕就会超出一个字节所能表示的数量范围了。因此,Java 虚拟机的指令集对于特定的操作只提供了有限的类型相关指令,换句话说,指令集将会故意设计成非完全独立的(not orthogonal,即并非每种数据类型和每一种操作都有对应的指令)。有一些单独的指令可以在必要的时候用来将一些不支持的类型转换为可支持的类型。
表 2-2 列举了 Java 虚拟机所支持的字节码指令集。用数据类型列所代表的特殊字符替换
opcode列的指令模板中的T,就可以得到一个具体的字节码指令。如果在表中指令模板与数据类型两列共同确定的单元格为空,则说明虚拟机不支持对这种数据类型执行这项操作。例如,load指令有操作int类型的iload,但是没有操作byte类型的同类指令。请注意,从表 2-2 中可以看出,大部分的指令都没有支持整数类型byte、char和short,甚至没有任何指令支持boolean类型。编译器会在编译期或运行期将byte和short类型的数据带符号扩展(sign-extend)为相应的int类型数据,将boolean和char类型数据零位扩展(zero-extend)为相应的int类型数据。与之类似,在处理boolean、byte、short和char类型的数组时,也会转换为使用对应的int类型的字节码指令来处理。因此,操作数的实际类型为boolean、byte、char及short的大多数操作,都可以用操作数的运算类型(computational type)为int的指令来完成。from : Java虚拟机规范(JavaSE8)
附表:


实际类型与关系映射表:

加载与存储指令集
加载本地变量到操作数栈的指令:
| 指令 | 描述 |
|---|---|
iload | 将 int 类型加载到操作数栈 |
iload <n> | 将指定索引的 int 类型加载到操作数栈 |
lload_<n> | 将指定索引的 long 类型加载到操作数栈 |
fload | 将 float 类型加载到操作数栈 |
fload <n> | 将指定索引的 float 类型加载到操作数栈 |
dload | 将 double 类型加载到操作数栈 |
dload <n> | 将指定索引的 double 类型加载到操作数栈 |
aload | 将引用类型加载到操作数栈 |
aload <n> | 将指定索引的引用类型加载到操作数栈 |
存储操作数栈到局部变量表的指令:
| 指令 | 描述 |
|---|---|
istore | 将 int 类型存储到局部变量表 |
istore <n> | 将 int 类型存储到指定索引的局部变量 |
lstore <n> | 将 long 类型存储到指定索引的局部变量 |
fstore | 将 float 类型存储到局部变量表 |
fstore <n> | 将 float 类型存储到指定索引的局部变量 |
dstore | 将 double 类型存储到局部变量表 |
dstore <n> | 将 double 类型存储到指定索引的局部变量 |
astore | 将引用类型存储到局部变量表 |
astore_<n> | 将引用类型存储到指定索引的局部变量 |
加载常量到操作数栈的指令:
| 指令 | 描述 |
|---|---|
bipush | 将带符号的 byte 常量(-128~127)加载到操作数栈 |
sipush | 将带符号的 short 常量(-32768~32767)加载到操作数栈 |
ldc | 将 int, float 或 String 类型的常量加载到操作数栈 |
ldc_w | 与 ldc 类似,但用于更大的常量池索引 |
ldc2_w | 将 long 或 double 类型的常量加载到操作数栈 |
aconst_null | 将 null 加载到操作数栈 |
iconst_m1 | 将整数 -1 加载到操作数栈 |
iconst <i> | 将整数常量加载到操作数栈 |
lconst <1> | 将长整数常量1加载到操作数栈 |
fconst <f> | 将浮点数常量加载到操作数栈 |
dconst <d> | 将双精度浮点数常量加载到操作数栈 |
扩充局部变量表的访问索引或立即数的指令:
| 指令 | 描述 |
|---|---|
wide | 扩展下一条指令使用的局部变量索引的宽度 |
算数指令集
算术指令:
| 操作类型 | 指令 |
|---|---|
| 加法 | iadd、ladd、fadd、dadd |
| 减法 | isub、Isub、fsub、dsub |
| 乘法 | imul、mul、fmul、dmul |
| 除法 | idiv、ldiv、fdiv、ddiv |
| 求余 | irem、Irem、frem、drem |
逻辑位运算指令:
| 操作类型 | 指令 |
|---|---|
| 按位或 | ior、lor |
| 按位与 | iand、land |
| 按位异或 | ixor、lxor |
其他运算指令:
| 操作类型 | 指令 |
|---|---|
| 求负值 | ineg、Ineg、fneg、dneg |
| 移位 | ishl、ishr、iushr、Ishl、Ishr、lushr |
| 局部变量自增 | iinc |
| 比较 | dcmpg、dcmpl、fcmpg、fcmpl、Icmp |
栈指令集字节码分析
main方法中的算数字节码
D:\CodeProjects\Eclipse\StackTest\bin\testjava>javap -c StackOneTest.class
Compiled from "StackOneTest.java"
public class testjava.StackOneTest {public testjava.StackOneTest();Code:0: aload_01: invokespecial #8 // Method java/lang/Object."<init>":()V4: returnpublic static void main(java.lang.String[]);Code:0: iconst_11: istore_12: iconst_23: istore_24: iload_15: iload_26: iadd7: istore_38: return
}
从上述字节码不难看出,JVM先执行了StackOneTest对象的构造,然后将这个对象变量加载到了操作数栈(默认构造器),我们重点来看main中的指令集:
0: iconst_1
1: istore_1
2: iconst_2
3: istore_2
先将两个常量1,2加载到操作数栈,并且进行了存储,接下来:
4: iload_1
5: iload_2
6: iadd
7: istore_3
加载了两个变量a,b到操作数栈,然后使用算数指令进行相加,最后进行存储结果(这里的第三个变量未使用,变量加载可能被编译器优化掉了),我们稍加修改:

可以发现在使用了第三个变量后,JVM进行了正确的变量加载
含方法的字节码分析
我们增加一个方法来进一步分析:
public class StackOneTest {public static int add(int a, int b) {return a+b;}public static void main(String[] args) {int a = 1;int b = 2;int c = add(a, b);System.out.println(c);}
}

从这里可知 invokestatic应该为调用方法的指令集,其运行也是和上一个大同小异,不过需要注意不同返回值类型的Treturn指令(T是借用泛型里的一个代号)
多次入栈字节码分析
修改代码:
public class StackOneTest {public static void main(String[] args) {int a = 1;int b = 2;int c = 3;int d = a+b;int e = a+c;}
}
相同方法进行分析:

可以发现其字节码也是逐行进行操作的,在变量d,e位置的处理和第一处也是类似的,只不过a进行了重复入栈
相关文章:
JVM入门到入土-Java虚拟机寄存器指令集与栈指令集
JVM入门到入土-Java虚拟机寄存器指令集与栈指令集 HotSpot虚拟机中的任何操作都需要入栈和出栈的步骤。 由于跨平台性的设计,Java的指令都是根据栈来设计的。不同平台CPU架构不同,所以不能设计为基于寄存器的。优点是跨平台,指令集小&#x…...
MS2244模拟开关可Pin to Pin兼容NJM2244
MS2244 是一款集成的视频开关,实现三输入视频或音频信号的三选一。可Pin to Pin兼容NJM2244。 芯片集成了 75Ω驱动电路,可以直接驱动电视监控器。芯片工作电压 5V~12V,带宽 10MHz,抗串扰 70dB (4.43MHz)。另外芯片还集…...
PostgreSQL 可观测性最佳实践
简介 软件简述 PostgreSQL 是一种开源的关系型数据库管理系统 (RDBMS),它提供了许多可观测性选项,以确保数据库的稳定性和可靠性。 可观测性 可观测性(Observability)是指对数据库状态和操作进行监控和记录,以便在…...
51单片机相关寄存器
前言 单片机复习的时候对应寄存器的记忆感觉很混乱,这里进行一下整理,后面的单词是我用来辅助记忆的,可能并不是表示原本的含义。 P3口的第二功能 0RXD 串行数据输入口 1TXD串行数据输出口2INT0外部中断0输入3INT1外部中断1输入4T0定时器0外部计数输入…...
二叉树进阶题目(超详解)
文章目录 前言根据二叉树创建字符串题目分析写代码 二叉树的层序遍历题目分析 写代码二叉树的层序遍历II题目分析写代码 二叉树的最近公共祖先题目分析写代码时间复杂度 优化思路优化的代码 二叉搜索树与双向链表题目分析写代码 从前序与中序遍历序列构造二叉树题目分析写代码从…...
W6100-EVB-Pico评估版介绍
文章目录 1 简介2 硬件资源2.1 硬件规格2.2 引脚定义2.3 工作条件 3 参考资料3.1 Datasheet3.2 原理图3.3 尺寸图(尺寸:mm)3.4 参考例程 4 硬件协议栈优势 1 简介 W6100-EVB-Pico是一款基于树莓派RP2040和全硬件TCP/IP协议栈以太网芯片W6100的…...
嵌入式面试准备
题目都摘于网上 嵌入式系统中经常要用到无限循环,如何用C编写死循环 while(1){}或者for(;😉 内存分区 代码区,全局区(全局变量,静态变量,以及常量),栈区,堆区 const关键…...
在Linux Docker中部署RStudio Server,实现高效远程访问
🌈个人主页:聆风吟 🔥系列专栏:网络奇遇记、Cpolar杂谈 🔖少年有梦不应止于心动,更要付诸行动。 文章目录 📋前言一. 安装RStudio Server二. 本地访问三. Linux 安装cpolar四. 配置RStudio serv…...
EternalBlue【永恒之蓝】漏洞详解(复现、演示、远程、后门、入侵、防御)内容丰富-深入剖析漏洞原理-漏洞成因-以及报错解决方法-值得收藏!
漏洞背景: 1.何为永恒之蓝? 永恒之蓝(Eternal Blue)爆发于2017年4月14日晚,是一种利用Windows系统的SMB协议漏洞来获取系统的最高权限,以此来控制被入侵的计算机。甚至于2017年5月12日, 不法分子…...
长链接与在线文件
什么是在线文件 常见的聊天工具,比如。。。微信,你可以发送一个文件给对端,即使对端不在线,这个文件也可以暂存在服务器上面,直到接收端上线消费或者超时,这个叫离线文件。与之对应的,在线文件要…...
Python内置数据类型等入门语(句)法
内置数据类型 数字(Number)关键字: int 、float、complex字符串(String)关键字:单引号,双引号 三引号都可以表示,8 种内置类型都可转为字符串类型列表(List) 关键符号 […...
ElasticSearch之RestClient笔记
1. ElasticSearch 1.1 倒排索引 1.2 ElasticSearch和Mysql对比 1.3 RestClient操作 导入依赖 <dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.15.…...
饥荒Mod 开发(二二):显示物品信息
饥荒Mod 开发(二一):超大便携背包,超大物品栏,永久保鲜 饥荒Mod 开发(二三):显示物品栏详细信息 饥荒中的物品没有详细信息,基本上只有一个名字,所以很多物品的功能都不知道,比如浆果吃了也不知…...
Microsoft Edge使用方法和心得
Microsoft Edge使用方法和心得 大家好,我是豪哥,一名来自杭州的Java程序员,今天我想分享一下我对Microsoft Edge的使用方法和心得。作为一名热爱编程的程序员,我发现一个高效的浏览器对于我们的工作和学习至关重要。而Microsoft …...
Kafka操作指令笔记
查堆积用命令查: ./kafka-consumer-groups.sh --bootstrap-server {kafka集群地址} --describe --group {消费组名称}bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 --describe --all-groups #查看所有组别的积压情况可以通过grep、awk或其他文…...
WAVE SUMMIT+ 2023倒计时2天,传文心一言将曝最新进展!
传文心一言将曝最新进展! 亮点一:趋势引领,“扛把子”文心一言将曝新进展亮点二:干货十足,硬核低门槛开发秘籍大放送亮点三:蓄势待发,大模型赋能产业正当时亮点四:群星闪耀ÿ…...
Crow:Middlewares 庖丁解牛5 context
Crow:Middlewares 庖丁解牛4 partial_context-CSDN博客 基于partial_context再来解释context namespace detail {template<typename... Middlewares>struct partial_context : public pop_back<Middlewares...>::template rebind<partial_context>, public…...
CentOS 7 设置网络
CentOS 7 设置网络 正常情况 ①登陆进去之后使用下面的命令修改文件 echo ONBOOTyes >> /etc/sysconfig/network-scripts/ifcfg-ens33②如果是虚拟机重启后使用如下命令进行查看IP地址 ip addr注:到这里如果显示有两部分,则代表网络设置成功&a…...
装饰器模式(Decorator)
装饰器模式(Decorator Pattern)是一种结构型设计模式,用于动态地给一个对象添加额外的职责。装饰器提供了一个灵活的替代扩展功能的方案,相比继承更加灵活。 在Java中,装饰器模式通常涉及以下几个部分: 组件(Component):定义一个对象接口,可以给这些对象动态添加职责…...
关于“Python”的核心知识点整理大全34
目录 第13 章 外星人 13.1 回顾项目 game_functions.py 13.2 创建第一个外星人 13.2.1 创建 Alien 类 alien.py 13.2.2 创建 Alien 实例 alien_invasion.py 13.2.3 让外星人出现在屏幕上 game_functions.py 13.3 创建一群外星人 13.3.1 确定一行可容纳…...
AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...
(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...
【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...
蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...
涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...
Java + Spring Boot + Mybatis 实现批量插入
在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法:使用 MyBatis 的 <foreach> 标签和批处理模式(ExecutorType.BATCH)。 方法一:使用 XML 的 <foreach> 标签ÿ…...
【网络安全】开源系统getshell漏洞挖掘
审计过程: 在入口文件admin/index.php中: 用户可以通过m,c,a等参数控制加载的文件和方法,在app/system/entrance.php中存在重点代码: 当M_TYPE system并且M_MODULE include时,会设置常量PATH_OWN_FILE为PATH_APP.M_T…...
