用Java手写jvm之模拟运行时数据区的虚拟机栈,局部变量表,操作数栈等

devtools/2024/9/22 15:40:57/

写在前面

下图是jvm的运行时数据区内存图:
在这里插入图片描述
,本文要模拟的是虚拟机栈的相关内存结构的交互过程。

1:正文

因为我们这里模拟的是线程执行方法调用的过程,所以这里先来定义一个线程对象:

java">public class Thread {// 程序计数器 program counterprivate int pc;// 线程虚拟机栈,每个线程都会被分配这样的一块内存区域private JvmStack stack;public Thread(){this.stack = new JvmStack(1024);}public void setStack(JvmStack stack) {this.stack = stack;}public int pc(){return this.pc;}public void setPC(int pc){this.pc = pc;}public void pushFrame(Frame frame){this.stack.push(frame);}public Frame popFrame(){return this.stack.pop();}public Frame currentFrame(){return this.stack.top();}}

这里的JvmStack就是给每个线程分配的线程栈,如下:

java">public class JvmStack {private int maxSize;private int size;// 线程虚拟机栈的方法的当前栈帧private Frame _top;// ...
}

Frame是线程栈的中栈帧,对应线程中的一个方法的调用,如下:

java">public class Frame {//stack is implemented as linked listFrame lower;//局部变量表private LocalVars localVars;//操作数栈private OperandStack operandStack;public Frame(int maxLocals, int maxStack) {this.localVars = new LocalVars(maxLocals);this.operandStack = new OperandStack(maxStack);}public LocalVars localVars(){return localVars;}public OperandStack operandStack(){return operandStack;}}

在栈帧中有一个指向下一个栈帧的引用,这样才能实现一个方法调用另一个方法。LocaVars就是栈帧的局部变量表,OperandStack是栈帧的操作数栈,如下:

java">public class LocalVars {private Slot[] slots;public LocalVars(int maxLocals) {if (maxLocals > 0) {slots = new Slot[maxLocals];for (int i = 0; i < maxLocals; i++) {slots[i] = new Slot();}}}// ...
}
java">public class OperandStack {private int size = 0;private Slot[] slots;public OperandStack(int maxStack) {if (maxStack > 0) {slots = new Slot[maxStack];for (int i = 0; i < maxStack; i++) {slots[i] = new Slot();}}}// ...
}

Slot是一个数据单元,表示在局部变量表以及操作数栈的一个数据项,如下:

java">/*** 本地变量表的数据槽,本地变量表就是一个数据槽组成的数组结构*/
public class Slot {// 槽数据public int num;//public Object ref;@Overridepublic String toString() {return "Slot{" +"num=" + num +", ref=" + ref +'}';}
}

接着我们编写代码来模拟如下代码的执行过程:

java">public class BBB {public static void main(String[] args) {int a = 9;int b = 8;int c = a + b;System.out.println(c);}
}

测试代码:

java">package com.dahuyou;import com.dahuyou.tryy.too.simulate.runtime.area.*;
import com.dahuyou.tryy.too.simulate.runtime.area.Thread;import java.util.Arrays;public class Mm {public static void main(String[] args) {// 模拟创建线程Thread thread = new Thread();// 创建线程的虚拟机栈JvmStack jvmStack = new JvmStack(1000);thread.setStack(jvmStack);// 模拟如下的方法作为当前栈帧/*public void fn() {int a = 9;int b = 8;int c = a + b;}*/// 指定本地变量表大小和操作数栈大小都是10,这里仅仅是测试了,不用在意这个值,jvm实际运行时会根据具体的方法来动态的设置这个值Frame frame = new Frame(10, 10);// 设置本地变量表LocalVars localVars = frame.localVars();localVars.setInt(0, 9);localVars.setInt(1 , 8);// 模拟代码执行过程/*ILOAD 1ILOAD 2IADDISTORE 3*/System.out.println("执行ILOAD 1指令将局部变量表2位置数据压到栈顶");OperandStack operandStack = frame.operandStack();operandStack.pushInt(localVars.getInt(0));System.out.println("执行ILOAD 2指令将局部变量表2位置数据压到栈顶");operandStack.pushInt(localVars.getInt(1));System.out.println("执行IADD指令,从操作数栈弹出2个数据,并执行加法");int param1 = operandStack.popInt();int param2 = operandStack.popInt();int result = param1 + param2;System.out.println("相加后的结果为:" + result);System.out.println("执行ISTORE 3指令将相加后的结果保存到局部变量表槽位3的位置");localVars.setInt(2, result);System.out.println("-----当前局部变量表的数据-----");System.out.println(Arrays.asList(localVars.getSlots()));}
}

运行:

执行ILOAD 1指令将局部变量表2位置数据压到栈顶
执行ILOAD 2指令将局部变量表2位置数据压到栈顶
执行IADD指令,从操作数栈弹出2个数据,并执行加法
相加后的结果为:17
执行ISTORE 3指令将相加后的结果保存到局部变量表槽位3的位置
-----当前局部变量表的数据-----
[Slot{num=9, ref=null}, Slot{num=8, ref=null}, Slot{num=17, ref=null}, Slot{num=0, ref=null}, Slot{num=0, ref=null}, Slot{num=0, ref=null}, Slot{num=0, ref=null}, Slot{num=0, ref=null}, Slot{num=0, ref=null}, Slot{num=0, ref=null}]Process finished with exit code 0

可以看到局部变量表第三个槽位存储的就是计算的结果17了。

写在后面

参考文章列表

jvm内存结构 。


http://www.ppmy.cn/devtools/86221.html

相关文章

解决 Git 访问 GitHub 时的 SSL 错误

引言 在使用 Git 进行版本控制时&#xff0c;我们可能会遇到各种网络相关的错误。其中一种常见的错误是 SSL 连接问题&#xff0c;这会导致 Git 无法访问远程仓库。本文将介绍一个具体的错误 OpenSSL SSL_read: SSL_ERROR_SYSCALL, errno 0&#xff0c;以及如何通过禁用 SSL 证…

Vue 3组件通信13种方法

文章目录 前言1. 父组件向子组件传递数据 (Props)父组件:子组件:2. 子组件向父组件传递数据 (Emit)子组件:父组件:3. 兄弟组件通信 (Mitt)发送事件的组件:接收事件的组件:4. 透传 Attributes ($attrs)父组件:子组件:5. 模板引用 (Refs)父组件:子组件:6. 双向绑定 (v-model)父组…

EXCEL 排名(RANK,COUNTIFS)

1.单列排序 需求描述&#xff1a;如有下面表格&#xff0c;需要按笔试成绩整体排名。 解决步骤&#xff1a; 我们使用RANK函数即可实现单列整体排名。 Number 选择第一列。 Ref 选择这一整列&#xff08;CtrlShift向下箭头、再按F4&#xff09;。 "确定"即可计算…

DATE_ADD、DATE_SUB Function - Mysql

DATE_ADD、DATE_SUB Function - SQL DATE_ADD() 和 DATE_SUB() 用于在日期或日期时间上增加或减少指定的时间间隔。 1. DATE_ADD() DATE_ADD() 函数用于向指定的日期或日期时间值添加一个时间间隔。 DATE_ADD(date, INTERVAL expr unit)date: 要添加时间间隔的日期或日期时间…

【Python数据结构与算法】递归----N皇后问题

题目&#xff1a;N皇后问题 描述 国际象棋的棋盘是由88共64个方格构成&#xff0c;棋子放在方格里面。如果两个皇后棋子在同一行、同一列&#xff0c;或者在某个正方形的对角线上&#xff0c;那么这两个皇后就会互相攻击。请在棋盘上摆放8个皇后&#xff0c;使得它们都不会互相…

论文《Few-Shot Object Detection with Model Calibration》的解读

《Few-Shot Object Detection with Model Calibration》论文的解读 作者&#xff1a;Qi Fan1, Chi-Keung Tang1 , and Yu-Wing Tai1,2 单位&#xff1a;1 The Hong Kong University of Science and Technology, 2 Kuaishou Technology 邮箱&#xff1a;fanqicsgmail.com, ckta…

OWASP ZAP:一款功能强大的开源Web安全扫描工具

目录 介绍 安装和配置 下载和安装 启动和配置 主界面概述 基本功能 自动扫描 手动测试 被动扫描 主动扫描 高级功能 代理功能 Fuzzer API 插件和扩展 使用场景 开发阶段 测试阶段 生产环境 实践案例 案例一&#xff1a;自动化扫描 案例二&#xff1a;手动…

源/目的检查开启导致虚拟IP背后的LVS无法正常访问

情况描述 近期发现48网段主机无法访问8.83这个VIP&#xff08;虚拟IP&#xff09;&#xff0c;环境是 8.83 绑定了两个LVS实例&#xff0c;然后LVS实例转发到后端的nginx 静态资源&#xff1b;整个流程是&#xff0c;客户端发起对VIP的请求&#xff0c;LVS将请求转发到后端实例…