JVM入门到入土-Java虚拟机寄存器指令集与栈指令集

news2025/2/5 5:09:18

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_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_1
       1: istore_1
       2: iconst_2
       3: istore_2
       4: iload_1
       5: iload_2
       6: iadd
       7: istore_3
       8: return
}

由于手里没有基于寄存器的指令集实验环境,可自行查阅查看方法

栈指令集字节码的语义

字节码指令集设计概述

在 Java 虚拟机的指令集中,大多数的指令都包含了其所操作的数据类型信息。例如,iload 指令用于从局部变量表中加载 int 类型的数据到操作数栈中,而 ad 指加载的则是 float 类型的数据。这两个指令的操作可能会是由同一段代码来实现的,但它们必须拥有各自独立的操作码。对于大部分与数据类型相关的字节码指令来说,它们的操作码助记符中都有特殊的字符来表明该指令为哪种数据类型服务:i 代表对 int 型的数据操作,l 代表 longs 代表 shortb 代表 bytec 代表 charf 代表 floatd 代表 doublea 代表 reference。也有一些指令的助记符没有明确用字母指明数据类型,例如 arraylength 指令,它没有代表数据类型的特殊字符,但操作数永远只能是一个数组类型的对象。还有另外一些指令,例如,无条件跳转指令 goto 则是与数据类型无关的。

因为 Java 虚拟机的操作码长度只有一个字节,所以包含了数据类型的操作码给指令集的设计带来了很大的压力。如果每一种与数据类型相关的指令都支持 Java 虚拟机的所有运行时数据类型,那恐怕就会超出一个字节所能表示的数量范围了。因此,Java 虚拟机的指令集对于特定的操作只提供了有限的类型相关指令,换句话说,指令集将会故意设计成非完全独立的(not orthogonal,即并非每种数据类型和每一种操作都有对应的指令)。有一些单独的指令可以在必要的时候用来将一些不支持的类型转换为可支持的类型。

表 2-2 列举了 Java 虚拟机所支持的字节码指令集。用数据类型列所代表的特殊字符替换 opcode 列的指令模板中的 T,就可以得到一个具体的字节码指令。如果在表中指令模板与数据类型两列共同确定的单元格为空,则说明虚拟机不支持对这种数据类型执行这项操作。例如,load 指令有操作 int 类型的 iload,但是没有操作 byte 类型的同类指令。请注意,从表 2-2 中可以看出,大部分的指令都没有支持整数类型 bytecharshort,甚至没有任何指令支持 boolean 类型。编译器会在编译期或运行期将 byteshort 类型的数据带符号扩展(sign-extend)为相应的 int 类型数据,将 booleanchar 类型数据零位扩展(zero-extend)为相应的 int 类型数据。与之类似,在处理 booleanbyteshortchar 类型的数组时,也会转换为使用对应的 int 类型的字节码指令来处理。因此,操作数的实际类型为 booleanbytecharshort 的大多数操作,都可以用操作数的运算类型(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扩展下一条指令使用的局部变量索引的宽度

算数指令集

算术指令:

操作类型指令
加法iaddladdfadddadd
减法isubIsubfsubdsub
乘法imulmulfmuldmul
除法idivldivfdivddiv
求余iremIremfremdrem

逻辑位运算指令:

操作类型指令
按位或iorlor
按位与iandland
按位异或ixorlxor

其他运算指令:

操作类型指令
求负值inegInegfnegdneg
移位ishlishriushrIshlIshrlushr
局部变量自增iinc
比较dcmpgdcmplfcmpgfcmplIcmp

栈指令集字节码分析

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_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_1
       1: istore_1
       2: iconst_2
       3: istore_2
       4: iload_1
       5: iload_2
       6: iadd
       7: istore_3
       8: return
}

从上述字节码不难看出,JVM先执行了StackOneTest对象的构造,然后将这个对象变量加载到了操作数栈(默认构造器),我们重点来看main中的指令集:

0: iconst_1
1: istore_1
2: iconst_2
3: istore_2

先将两个常量12加载到操作数栈,并且进行了存储,接下来:

4: iload_1
5: iload_2
6: iadd
7: istore_3

加载了两个变量ab到操作数栈,然后使用算数指令进行相加,最后进行存储结果(这里的第三个变量未使用,变量加载可能被编译器优化掉了),我们稍加修改:

在这里插入图片描述

可以发现在使用了第三个变量后,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;
	}
}

相同方法进行分析:

在这里插入图片描述

可以发现其字节码也是逐行进行操作的,在变量de位置的处理和第一处也是类似的,只不过a进行了重复入栈

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

k8s---kubernets

目录 一、Kurbernetes 1.2、K8S的特性&#xff1a; 1.3、docker和K8S&#xff1a; 1.4、K8S的作用&#xff1a; 1.5、K8S的特性&#xff1a; 二、K8S集群架构与组件&#xff1a; 三、K8S的核心组件&#xff1a; 一、master组件&#xff1a; 1、kube-apiserver&#xff1…

【Spring实战】07 JPA

文章目录 1. 定义2. 出现原因3. 添加依赖4. 使用1&#xff09;创建 Repository 接口2&#xff09;自定义查询方法&#xff08;非必须&#xff09;3&#xff09;创建实体类4&#xff09;调用方法 5. 验证6. 优点7. 缺点8. 详细代码总结 1. 定义 Spring Data JPA 是 Spring 提供…

C# 编写简单二维码条形码工具

C# 二维码条形码工具 该工具简单实现了二维码条形码生成与识别功能&#xff0c;识别方式&#xff1a;通过摄像头实时识别或通过图片文件识别。 using AForge.Genetic; using AForge.Video.DirectShow; using System; using System.Collections.Generic; using System.Component…

实习知识整理6:前后端利用jQuery $.ajax数据传输的四种方式

方式1&#xff1a;前端发送key/value(String字符串)&#xff0c;后台返回文本 前端&#xff1a; <input id"test1" type"button" value"前端发送key/value(String字符串)&#xff0c;后台返回文本"/> $(function() {$("#test1&quo…

YHZ001 Python 简介

配套视频链接: YHZ001 Python 简介 目录 &#x1f649; Python的历史&#x1fab1; Python的作者&#x1f98a; Python 的优缺点&#x1f417; Python 的应用领域&#x1f41e; Python 哲学&#x1f430; Python 解释器 &#x1f649; Python的历史 1989年圣诞节&#xff1a; …

数据智慧:C#中编程实现自定义计算的Excel数据透视表

前言 数据透视表&#xff08;Pivot Table&#xff09;是一种数据分析工具&#xff0c;通常用于对大量数据进行汇总、分析和展示。它可以帮助用户从原始数据中提取关键信息、发现模式和趋势&#xff0c;并以可视化的方式呈现。 在数据透视表中&#xff0c;数据分析师通常希望进…

Redis Streams在Spring Boot中的应用:构建可靠的消息队列解决方案【redis实战 二】

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 Redis Streams在Spring Boot中的应用&#xff1a;构建可靠的消息队列解决方案 引言前言Redis Streams的基本概念和特性1. 日志数据结构2. 消息和字段3. 消费者组4. 消息ID5. 实时和历史数据处理6. 性能…

1.决策树

目录 1. 什么是决策树? 2. 决策树的原理 2.1 如何构建决策树&#xff1f; 2.2 构建决策树的数据算法 2.2.1 信息熵 2.2.2 ID3算法 2.2.2.1 信息的定义 2.2.2.2 信息增益 2.2.2.3 ID3算法举例 2.2.2.4 ID3算法优缺点 2.2.3 C4.5算法 2.2.3.1 C4.5算法举例 2.2.4 CART算法 2.2.4…

智能优化算法应用:基于孔雀算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于孔雀算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于孔雀算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.孔雀算法4.实验参数设定5.算法结果6.参考文献7.MA…

机械革命极光Pro重装Win10系统图解

机械革命极光Pro是性能优秀的笔记本电脑&#xff0c;深受广大用户的喜欢&#xff0c;现在用户想给笔记本电脑重新安装一下操作系统&#xff0c;但不知道重装系统的详细步骤。下面小编将带来机械革命极光Pro笔记本电脑重装系统Win10版本的步骤介绍&#xff0c;帮助更多的用户完成…

Python 基础面试第三弹

1. 获取当前目录下所有文件名 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import os def get_all_files(directory): file_list []<br> # <code>os.walk</code>返回一个生成器&#xff0c;每次迭代时返回当前目录路径、子目录列表和文件列表 for…

【Kafka】Kafka客户端认证失败:Cluster authorization failed.

背景 kafka客户端是公司内部基于spring-kafka封装的spring-boot版本&#xff1a;3.xspring-kafka版本&#xff1a;2.1.11.RELEASE集群认证方式&#xff1a;SASL_PLAINTEXT/SCRAM-SHA-512经过多年的经验&#xff0c;以及实际验证&#xff0c;配置是没问题的&#xff0c;但是业务…

数据结构:图文详解 树与二叉树(树与二叉树的概念和性质,存储,遍历)

目录 一.树的概念 二.树中重要的概念 三.二叉树的概念 满二叉树 完全二叉树 四.二叉树的性质 五.二叉树的存储 六.二叉树的遍历 前序遍历 中序遍历 后序遍历 一.树的概念 树是一种非线性数据结构&#xff0c;它由节点和边组成。树的每个节点可以有零个或多个子节点…

113基于matlab的PSO-SVM多输入单输出预测程序

基于matlab的PSO-SVM多输入单输出预测程序。PSO对SVM的两个参数进行优化得到最佳参数值进行预测。并输出预测误差等相应结果。程序已调通&#xff0c;可直接运行。 113matlabPSO-SVM多输入单输出 (xiaohongshu.com)

普中STM32-PZ6806L开发板(STM32CubeMX创建项目并点亮LED灯)

简介 搭建一个用于驱动 STM32F103ZET6 GPIO点亮LED灯的任务;电路原理图 LED电路原理图 芯片引脚连接LED驱动引脚原理图 创建一个点亮LED灯的Keil 5项目 创建STM32CubeMX项目 New Project -> 单击 -> 芯片搜索STM32F103ZET6->双击创建 初始化时钟 初始化LED G…

基于双闭环PI的SMO无速度控制系统simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于双闭环PI的SMO无速度控制系统simulink建模与仿真&#xff0c;基于双闭环PI的SMO无速度控制系统主要由两个闭环组成&#xff1a;一个是电流环&#xff0c;另一个是速度环。…

Flink CDC 1.0至3.0回忆录

Flink CDC 1.0至3.0回忆录 一、引言二、CDC概述三、Flink CDC 1.0&#xff1a;扬帆起航3.1 架构设计3.2 版本痛点 四、Flink CDC 2.0&#xff1a;成长突破4.1 DBlog 无锁算法4.2 FLIP-27 架构实现4.3 整体流程 五、Flink CDC 3.0&#xff1a;应运而生六、Flink CDC 的影响和价值…

数据库原理及应用·数据库保护

7.1 事务 7.1.1 事务定义 1.事务是用户定义的一个数据操作序列&#xff0c;这些操作要么全部执行、要么全部不执行&#xff0c;是一个不可分割的工作单元。 事务是恢复和并发控制的基本单位 事务的两种方式&#xff1a; 7.1.2 事务处理模型 1.ISO事务处理模型&#xff1a…

隐私第一:在几分钟内部署本地大语言模型!

彻底改变您的数据安全游戏&#xff1a;快速无缝部署本地大语言模型&#xff0c;实现无与伦比的隐私! 2023年是人工智能领域加速发展的一年。除了健壮的商业上可用的大型语言模型之外&#xff0c;还出现了许多值得称赞的开源方案&#xff0c;例如Llama2、Codellama、Mistral和Vi…

鸿蒙开发中的坑(持续更新……)

最近在使用鸿蒙开发时&#xff0c;碰到了一些坑&#xff0c;特做记录&#xff0c;如&#xff1a;鸿蒙的preview不能预览&#xff0c;轮播图组件Swiper使用时的问题&#xff0c;console.log() 打印的内容 一、鸿蒙的preview不能预览 首先&#xff0c;只有 ets文件才能预览。 其…