项目地址:https://github.com/gongxianshengjiadexiaohuihui
在运行java程序时,Java虚拟机需要使用内存来存放各种各样的数据,Java虚拟机规范把这些内存的区域叫做运行时数据区。运行时数据区可以分为两类:一类是多个线程共享的,另一类是线程私有的。多线程共享的运行时数据区需要在java虚拟机启动时创建,在java虚拟机退出时销毁。线程私有的运行时数据区则在创建线程时才创建,线程退出时销毁。
多线程共享的内存区域主要存放两类数据:类数据和类实例对象。对象数据放在堆中,类数据放在方法区中。堆由垃圾回收器定期清理,所以程序员不需要关心对象空间的释放。类数据包括字段信息和方法信息,方法的字节码,运行时常量池。从逻辑上讲,方法区也是堆的一部分。
线程私有的运行时数据区用于辅助执行java字节码,每个线程都有自己的pc寄存器,和java虚拟机栈。java虚拟机栈又有栈帧构成,帧中保存方法执行的状态。包括局部变量表和操作数栈。在某一时刻,某一线程肯定是在执行某个方法。这个方法叫做当前方法,执行该方法的帧,叫做线程的当前帧。
java虚拟机支持的数据类型
我们来根据这个图实现线程私有的运行时数据区
首先是Thread
package rtda
type Thread struct {
pc int
stack *Stack
}
//构造函数
func NewThread() *Thread{
return &Thread{
stack: newStack(1024),
}
}
//setter&getter
func (self *Thread)PC() int{
return self.pc
}
func (self *Thread)SetPC(pc int){
self.pc = pc
}
//操作栈帧
func (self *Thread)PushFrame(frame *Frame){
self.stack.push(frame)
}
func (self *Thread)PopFrame() *Frame{
return self.stack.pop()
}
func (self *Thread)CurrentFrame() *Frame{
return self.stack.top()
}
从上层往下层,接下来构造jvm_stack
package rtda
type Stack struct {
//最大容量
maxSize uint
//当前数量
size uint
//这是一个链表结构,_top相当于头指针
_top *Frame
}
//构造函数
func newStack(maxSize uint) *Stack {
return &Stack{
maxSize: maxSize,
}
}
//出栈进栈操作
func (self *Stack) push(frame *Frame) {
if self.size >= self.maxSize {
panic("java.lang.StackOverflowError")
}
if self._top != nil {
frame.lower = self._top
}
self._top = frame
self.size++
}
func (self *Stack) pop() *Frame {
if self._top == nil {
panic("jvm stack is empty!")
}
top := self._top
self._top = top.lower
top.lower = nil
self.size--
return top
}
func (self *Stack) top() *Frame {
if self._top == nil {
panic("jvm stack is empty!")
}
return self._top
}
接下来一层是我们的栈帧
package rtda
type Frame struct {
lower *Frame
localVars LocalVars
operandStack *OperandStack
}
func NewFrame(maxLocals, maxStack uint)*Frame{
return &Frame{
localVars : newLocalVars(maxLocals),
operandStack : newOperandStack(maxStack),
}
}
//getter
func (self *Frame)LocalVars()LocalVars{
return self.localVars
}
func (self *Frame)OperandStack()*OperandStack{
return self.operandStack
}
在构造局部变量表和操作数栈之前,我们要准备两样东西
一个是object,因为我们操作的数据不仅有基本类型,还有引用,因为具体object我们会在后面实现,在次先创建一个内容为空的object
package rtda
type Object struct {
}
局部变量表是按索引访问的,类似于按照数组下标访问的,这个数组的每个元素至少可以容纳一个int值或引用值。我们想到可以用一个结构体去表示一个int值和一个引用,需要什么值,取什么值即可。
package rtda
type Slot struct {
num int32
ref *Object
}
局部变量表
因为局部变量表存放的是int32,所以存放float,double,long的时候,需要我们进行转换
package rtda
import "math"
type LocalVars []Slot
func newLocalVars(maxlocals uint) LocalVars{
if maxlocals > 0{
return make([]Slot,maxlocals)
}
return nil
}
func (self LocalVars) SetInt(index uint, val int32){
self[index].num = val
}
func (self LocalVars) GetInt(index uint) int32{
return self[index].num
}
func (self LocalVars) SetFloat(index uint, val float32){
bits := math.Float32bits(val)
self[index].num = int32(bits)
}
func (self LocalVars)GetFloat(index uint) float32{
bits := uint32(self[index].num)
return math.Float32frombits(bits)
}
func (self LocalVars)SetLong(index uint, val int64){
self[index].num = int32(val)
self[index+1].num = int32(val >> 32)
}
func (self LocalVars)GetLong(index uint) int64{
low := uint32(self[index].num)
high := uint32(self[index+1].num)
return int64(high)<<32 |int64(low)
}
func (self LocalVars)SetDouble(index uint, val float64){
bits := math.Float64bits(val)
self.SetLong(index,int64(bits))
}
func (self LocalVars)GetDouble(index uint)float64{
bits := (uint64)(self.GetLong(index))
return math.Float64frombits(bits)
}
func (self LocalVars)SetRef(index uint, ref *Object){
self[index].ref = ref
}
func (self LocalVars)GetRef(index uint)*Object{
return self[index].ref
}
操作数栈
package rtda
import "math"
//操作数栈 属于栈帧 一个方法执行就对应一个
//操作数栈大小是编译器编译时就确定的 在class文件的方法信息的code属性里面有写这项
type OperandStack struct {
size uint
slots []Slot
}
func newOperandStack(maxStack uint)*OperandStack{
if maxStack > 0{
return &OperandStack{
slots : make([]Slot,maxStack),
}
}
return nil
}
func (self *OperandStack)PushInt(val int32){
self.slots[self.size].num = val
self.size ++
}
func (self *OperandStack)PopInt() int32{
self.size --
return self.slots[self.size].num
}
func (self *OperandStack)PushFloat(val float32){
bits := math.Float32bits(val)
self.slots[self.size].num = int32(bits)
self.size ++
}
func (self *OperandStack)PopFloat()float32 {
self.size --
bits := uint32(self.slots[self.size].num)
return math.Float32frombits(bits)
}
func (self *OperandStack)PushLong(val int64){
self.slots[self.size].num = int32(val)
self.slots[self.size+1].num = int32(val >> 32)
self.size += 2
}
func (self *OperandStack)PopLong()int64{
self.size -= 2
low := uint32(self.slots[self.size].num)
high := uint32(self.slots[self.size+1].num)
return int64(high) << 32 | int64(low)
}
func (self *OperandStack)PushDouble(val float64){
bits := math.Float64bits(val)
self.PushLong(int64(bits))
}
func (self *OperandStack)PopDouble()float64{
bits := uint64(self.PopLong())
return math.Float64frombits(bits)
}
func (self *OperandStack)PushRef(ref *Object){
self.slots[self.size].ref = ref
self.size ++
}
func(self *OperandStack)PopRef() *Object{
self.size --
return self.slots[self.size].ref
}
最后改造我们的main函数
package main
import "fmt"
import "jvmgo/rtda"
func main(){
//调用解析命令行的行数,接受解析结果
cmd:=parseCmd()
if cmd.versionFlag{
fmt.Println("version 0.0.1")
}else if cmd.helpFlag||cmd.class==""{
printUsage()
}else{
startJVM(cmd)
}
}
func startJVM(cmd *Cmd){
frame := rtda.NewFrame(100,100)
testLocalVars(frame.LocalVars());
testOperandStack(frame.OperandStack());
}
func testLocalVars(vars rtda.LocalVars) {
vars.SetInt(0, 100)
vars.SetInt(1, -100)
vars.SetLong(2, 2997924580)
vars.SetLong(4, -2997924580)
vars.SetFloat(6, 3.1415926)
vars.SetDouble(7, 2.71828182845)
vars.SetRef(9, nil)
println(vars.GetInt(0))
println(vars.GetInt(1))
println(vars.GetLong(2))
println(vars.GetLong(4))
println(vars.GetFloat(6))
println(vars.GetDouble(7))
println(vars.GetRef(9))
}
func testOperandStack(ops *rtda.OperandStack) {
ops.PushInt(100)
ops.PushInt(-100)
ops.PushLong(2997924580)
ops.PushLong(-2997924580)
ops.PushFloat(3.1415926)
ops.PushDouble(2.71828182845)
ops.PushRef(nil)
println(ops.PopRef())
println(ops.PopDouble())
println(ops.PopFloat())
println(ops.PopLong())
println(ops.PopLong())
println(ops.PopInt())
println(ops.PopInt())
}
运行结果
参考资料:《自己动手写Java虚拟机》