用go写jvm之运行时数据区

在运行Java程序时,Java虚拟机需要使用内存来存放各式各样的数据。Java虚拟机规范把这些内存区域叫做运行时数据区。

概述

运行时数据区分为:一类是多线程共享的,另一类则是线程私有的。

多线程共享的内存区域:
主要存放类数据和类实例,对象数据存放在堆Heap,类数据存放在方法区Method Area。
堆由垃圾收集器定期清理。
类数据包括字段和方法信息、方法的字节码、运行时常量池等。

线程私有的运行时数据区:
主要用来辅助执行Java字节码。每个线程都有字节的pc寄存器和Java虚拟机栈(JVM Stack)。
Java虚拟机栈又由栈帧组成,帧中保存方法执行的状态,包括局部变量表和操作数栈等。

在任一时刻,某一线程在执行某个方法,这个方法叫做当前方法,执行方法的帧叫做线程的当前帧,声明该方法的类叫做当前类。

实现线程私有运行时数据区

在这里,我们先实现线程私有运行时数据区,大致思路为创建pc和虚拟栈,其中栈帧包含局部变量表和操作数栈。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Thread 线程
// 每个线程都有一个pc寄存器和Java虚拟机栈
type Thread struct {
pc int //pc寄存器
stack *Stack //Java虚拟机栈的指针
}

// Stack 栈
type Stack struct {
// 最大容量
maxSize uint
// 当前大小
size uint
// 栈顶
_top *Frame
}

type Frame struct {
lower *Frame //下一个栈帧
localVars LocalVars //局部变量表,使用[]Slot实现
operandStack *OperandStack //操作数栈,也使用[]Slot实现
}

讨论局部变量表和操作数栈

局部变量表和操作数栈的元素,需要至少表示一个int或引用值,用来存放数据。
如果只用一个int,引用值不能存放,会被垃圾回收清理掉,用[]interface{}表示太过繁琐。
所以,我们使用Slot来表示局部变量表和操作数栈的元素,Slot是一个结构体,包含int和引用值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Slot 局部变量,用来实现局部变量表
type Slot struct {
num int32 //存放整数,将基础变量类型转换成int32类型
ref *Object //存放引用,将引用类型转换成*Object类型
}

// LocalVars 局部变量表
type LocalVars []Slot

// OperandStack 操作数栈
type OperandStack struct {
size uint //栈顶位置
slots []Slot //栈
}

相关方法

在这里,我们的局部变量表和操作数栈用来存放数据,根据不同类型,采用不同存放方式。

对于java虚拟栈,我们需要实现push和pop方法,用来入栈和出栈。

代码详见这里

总结

在这里,我们实现了线程私有的运行时数据区,包括pc寄存器和Java虚拟机栈,其中Java虚拟机栈由栈帧组成,栈帧包含局部变量表和操作数栈。