《自己動手寫java虛擬機器》學習筆記(七)-----執行緒私有執行時資料區(go)
專案地址: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虛擬機器》