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 确定一行可容纳…...

19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...

Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...

循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
Go 语言接口详解
Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...

1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...