写在前面
下图是jvm的运行时数据区内存图:
,本文要模拟的是虚拟机栈的相关内存结构的交互过程。
1:正文
因为我们这里模拟的是线程执行方法调用的过程,所以这里先来定义一个线程对象:
public class Thread {
// 程序计数器 program counter
private 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就是给每个线程分配的线程栈,如下:
public class JvmStack {
private int maxSize;
private int size;
// 线程虚拟机栈的方法的当前栈帧
private Frame _top;
// ...
}
Frame是线程栈的中栈帧,对应线程中的一个方法的调用,如下:
public class Frame {
//stack is implemented as linked list
Frame 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是栈帧的操作数栈,如下:
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();
}
}
}
// ...
}
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是一个数据单元,表示在局部变量表以及操作数栈的一个数据项,如下:
/**
* 本地变量表的数据槽,本地变量表就是一个数据槽组成的数组结构
*/
public class Slot {
// 槽数据
public int num;
//
public Object ref;
@Override
public String toString() {
return "Slot{" +
"num=" + num +
", ref=" + ref +
'}';
}
}
接着我们编写代码来模拟如下代码的执行过程:
public class BBB {
public static void main(String[] args) {
int a = 9;
int b = 8;
int c = a + b;
System.out.println(c);
}
}
测试代码:
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 1
ILOAD 2
IADD
ISTORE 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内存结构 。