代码、内容参考来自于张秀宏大佬的自己动手写Java虚拟机 (Java核心技术系列)以及尚硅谷宋红康:JVM全套教程。
1.存储指令
出栈装入局部变量表指令用于将操作数栈中栈顶元素弹出后,装入局部变量表的指定位置,用于给局部变量赋值。
这类指令主要以xstore的形式存在,比如xstore (x为i、l、f、d、 a)、 xstore_n (x为i、l、f、d、 a, n为0 至 3)。
- 其中,指令istore_n将从操作数栈中弹出一个整数,并把它赋值给局部变量索引n位置。
- 指令xstore由于没有隐含参数信息,故需要提供一个byte类型的参数类指定目标局部变量表的位置。
说明:
- 一般说来,类似像store这样的命令需要带一个参数,用来指明将弹出的元素放在局部变量表的第几个位置。但是,为了尽可能压缩指令大小,使用专门的istore_1指令表示将弹出的元素放置在局部变量表第1个位置。类似的还有istore_0、 istore_2、 istore_3,它们分别表示从操作数栈顶弹出一个元素,存放在局部变量表第0、2、3个位置。
- 由于局部变量表前几个位置总是非常常用,因此这种做法虽然增加了指令数量,但是可以大大压缩生成的字节码的体积,如果局部变量表很大,需要存储的槽位大于3,那么可以使用istore指令,外加一个参数,用来表示需要存放的槽位位置。
和加载指令刚好相反,存储指令把变量从操作数栈顶弹出,然后存入局部变量表。和加载指令一样,存储指令也可以分为6类。
整体代码
在instructions\stores目录下创建lstore.go文件,在其中定义5条指令,代码如下:
package stores
import (
"jvmgo/ch05/instructions/base"
"jvmgo/ch05/rtda"
)
// Store long into local variable
type LSTORE struct{ base.Index8Instruction }
func (self *LSTORE) Execute(frame *rtda.Frame) {
_lstore(frame, uint(self.Index))
}
type LSTORE_0 struct{ base.NoOperandsInstruction }
func (self *LSTORE_0) Execute(frame *rtda.Frame) {
_lstore(frame, 0)
}
type LSTORE_1 struct{ base.NoOperandsInstruction }
func (self *LSTORE_1) Execute(frame *rtda.Frame) {
_lstore(frame, 1)
}
type LSTORE_2 struct{ base.NoOperandsInstruction }
func (self *LSTORE_2) Execute(frame *rtda.Frame) {
_lstore(frame, 2)
}
type LSTORE_3 struct{ base.NoOperandsInstruction }
func (self *LSTORE_3) Execute(frame *rtda.Frame) {
_lstore(frame, 3)
}
func _lstore(frame *rtda.Frame, index uint) {
val := frame.OperandStack().PopLong()
frame.LocalVars().SetLong(index, val)
}在instructions\stores目录下创建astore.go文件,代码如下:
package stores
import (
"jvmgo/ch05/instructions/base"
"jvmgo/ch05/rtda"
)
// Store reference into local variable
type ASTORE struct{ base.Index8Instruction }
func (self *ASTORE) Execute(frame *rtda.Frame) {
_astore(frame, uint(self.Index))
}
type ASTORE_0 struct{ base.NoOperandsInstruction }
func (self *ASTORE_0) Execute(frame *rtda.Frame) {
_astore(frame, 0)
}
type ASTORE_1 struct{ base.NoOperandsInstruction }
func (self *ASTORE_1) Execute(frame *rtda.Frame) {
_astore(frame, 1)
}
type ASTORE_2 struct{ base.NoOperandsInstruction }
func (self *ASTORE_2) Execute(frame *rtda.Frame) {
_astore(frame, 2)
}
type ASTORE_3 struct{ base.NoOperandsInstruction }
func (self *ASTORE_3) Execute(frame *rtda.Frame) {
_astore(frame, 3)
}
func _astore(frame *rtda.Frame, index uint) {
ref := frame.OperandStack().PopRef()
frame.LocalVars().SetRef(index, ref)
}
在instructions\stores目录下创建dstore.go文件,代码如下:
package stores
import (
"jvmgo/ch05/instructions/base"
"jvmgo/ch05/rtda"
)
// Store double into local variable
type DSTORE struct{ base.Index8Instruction }
func (self *DSTORE) Execute(frame *rtda.Frame) {
_dstore(frame, uint(self.Index))
}
type DSTORE_0 struct{ base.NoOperandsInstruction }
func (self *DSTORE_0) Execute(frame *rtda.Frame) {
_dstore(frame, 0)
}
type DSTORE_1 struct{ base.NoOperandsInstruction }
func (self *DSTORE_1) Execute(frame *rtda.Frame) {
_dstore(frame, 1)
}
type DSTORE_2 struct{ base.NoOperandsInstruction }
func (self *DSTORE_2) Execute(frame *rtda.Frame) {
_dstore(frame, 2)
}
type DSTORE_3 struct{ base.NoOperandsInstruction }
func (self *DSTORE_3) Execute(frame *rtda.Frame) {
_dstore(frame, 3)
}
func _dstore(frame *rtda.Frame, index uint) {
val := frame.OperandStack().PopDouble()
frame.LocalVars().SetDouble(index, val)
}
在instructions\stores目录下创建fstore.go文件,代码如下:
package stores
import (
"jvmgo/ch05/instructions/base"
"jvmgo/ch05/rtda"
)
// Store float into local variable
type FSTORE struct{ base.Index8Instruction }
func (self *FSTORE) Execute(frame *rtda.Frame) {
_fstore(frame, uint(self.Index))
}
type FSTORE_0 struct{ base.NoOperandsInstruction }
func (self *FSTORE_0) Execute(frame *rtda.Frame) {
_fstore(frame, 0)
}
type FSTORE_1 struct{ base.NoOperandsInstruction }
func (self *FSTORE_1) Execute(frame *rtda.Frame) {
_fstore(frame, 1)
}
type FSTORE_2 struct{ base.NoOperandsInstruction }
func (self *FSTORE_2) Execute(frame *rtda.Frame) {
_fstore(frame, 2)
}
type FSTORE_3 struct{ base.NoOperandsInstruction }
func (self *FSTORE_3) Execute(frame *rtda.Frame) {
_fstore(frame, 3)
}
func _fstore(frame *rtda.Frame, index uint) {
val := frame.OperandStack().PopFloat()
frame.LocalVars().SetFloat(index, val)
}
在instructions\stores目录下创建istore.go文件,代码如下:
package stores
import (
"jvmgo/ch05/instructions/base"
"jvmgo/ch05/rtda"
)
// Store int into local variable
type ISTORE struct{ base.Index8Instruction }
func (self *ISTORE) Execute(frame *rtda.Frame) {
_istore(frame, uint(self.Index))
}
type ISTORE_0 struct{ base.NoOperandsInstruction }
func (self *ISTORE_0) Execute(frame *rtda.Frame) {
_istore(frame, 0)
}
type ISTORE_1 struct{ base.NoOperandsInstruction }
func (self *ISTORE_1) Execute(frame *rtda.Frame) {
_istore(frame, 1)
}
type ISTORE_2 struct{ base.NoOperandsInstruction }
func (self *ISTORE_2) Execute(frame *rtda.Frame) {
_istore(frame, 2)
}
type ISTORE_3 struct{ base.NoOperandsInstruction }
func (self *ISTORE_3) Execute(frame *rtda.Frame) {
_istore(frame, 3)
}
func _istore(frame *rtda.Frame, index uint) {
val := frame.OperandStack().PopInt()
frame.LocalVars().SetInt(index, val)
}
代码解释
以lstore系列指令为例进行介绍。
同样定义一个函数供5条指令使用,代码如下:

lstore指令的索引来自操作数,其Execute()方法如下:

其余4条指令的索引隐含在操作码中,以lstore_2为例,其

2.栈指令
操作数栈管理指令
如同操作一个普通数据结构中的堆栈那样,JVM提供的操作数栈管理指令,可以用于直接操作操作数栈的指令。
这类指令包括如下内容:
- 将一个或两个元素从栈顶弹出,并且直接废弃: pop, pop2;
- 复制栈顶一个或两个数值并将复制值或双份的复制值重新压入栈顶: dup,dup2,dup_x1,dup2_x1,dup_x2,dup2_x2;
- 将栈最顶端的两个Slot数值位置交换: swap,Java虚拟机没有提供交换两个64位数据类型(long、 double)数值的指令。
- 指令nop,是一个非常特殊的指令,它的字节码为0x00,和汇编语言中的nop一样,它表示什么都不做,这条指令一般可用于调试、占位等。
这些指令属于通用型,对栈的压入或者弹出无需指明数据类型。
说明:
1.不带_x的指令是复制栈顶数据并压入栈顶。包括两个指令, dup和dup2。dup的系数代表要复制的Slot个数。
- dup开头的指令用于复制1个Slot(4个字节)的数据。例如1个int或1个reference类型数据
- dup2开头的指令用于复制2个Slot的数据。例如1个long,或2个int,或1个int+1个float类型
2.带_x的指令是复制栈顶数据并插入栈顶以下的某个位置。共有4个指令,dup_x1, dup2_x1,dup_x2, dup2_x2。对于带_x的复制插入指令,只要将指令的dup和x的系数相加,结果即为需要插入的位置。因此
- dup_x1插入位置:1+1=2,即栈顶2个Slot下面
- dup_x2插入位置: 1+2=3,即栈顶3个Slot下面
- dup2_x1插入位置: 2+1=3,即找顶3个Slot下面
- dup2_x2插入位置: 2+2=4,即栈顶4个Slot下面
3.pop:将栈顶的1个Slot数值出栈。例如1个short类型数值。pop2:将栈项的2个slot数值出栈。例如1个double类型数值,或者2个int类型数值。
栈指令直接对操作数栈进行操作,
共9条:
- pop和pop2指令将栈顶变量弹出,
- dup系列指令复制栈顶变量,
- swap指令交换栈顶的两个变量。
和其他类型的指令不同,栈指令并不关心变量类型。为了实现栈指令,需要给OperandStack结构体添加两个方法。
打开rtda\operand_stack.go文件,在其中加入PushSlot()和PopSlot()方法,代码如下:
func (self *OperandStack) PushSlot(slot Slot) {
self.slots[self.size] = slot
self.size++
}
func (self *OperandStack) PopSlot() Slot {
self.size--
return self.slots[self.size]
}
pop和pop2指令
整体代码
在instructions\stack目录下创建pop.go文件,在其中定义pop和pop2指令,代码如下:
package stack
import (
"jvmgo/ch05/instructions/base"
"jvmgo/ch05/rtda"
)
// Pop the top operand stack value
type POP struct{ base.NoOperandsInstruction }
/*
bottom -> top
[...][c][b][a]
|
V
[...][c][b]
*/
func (self *POP) Execute(frame *rtda.Frame) {
stack := frame.OperandStack()
stack.PopSlot()
}
// Pop the top one or two operand stack values
type POP2 struct{ base.NoOperandsInstruction }
/*
bottom -> top
[...][c][b][a]
| |
V V
[...][c]
*/
func (self *POP2) Execute(frame *rtda.Frame) {
stack := frame.OperandStack()
stack.PopSlot()
stack.PopSlot()
}
代码解释
pop指令把栈顶变量弹出

pop指令只能用于弹出int、float等占用一个操作数栈位置的变量。double和long变量在操作数栈中占据两个位置,需要使用pop2指令弹出

dup指令
整体代码
在instructions\stack目录下创建dup.go文件,在其中定义6条指令,代码如下:
package stack
import (
"jvmgo/ch05/instructions/base"
"jvmgo/ch05/rtda"
)
// Duplicate the top operand stack value
type DUP struct{ base.NoOperandsInstruction }
/*
bottom -> top
[...][c][b][a]
\_
|
V
[...][c][b][a][a]
*/
func (self *DUP) Execute(frame *rtda.Frame) {
stack := frame.OperandStack()
slot := stack.PopSlot()
stack.PushSlot(slot)
stack.PushSlot(slot)
}
// Duplicate the top operand stack value and insert two values down
type DUP_X1 struct{ base.NoOperandsInstruction }
/*
bottom -> top
[...][c][b][a]
__/
|
V
[...][c][a][b][a]
*/
func (self *DUP_X1) Execute(frame *rtda.Frame) {
stack := frame.OperandStack()
slot1 := stack.PopSlot()
slot2 := stack.PopSlot()
stack.PushSlot(slot1)
stack.PushSlot(slot2)
stack.PushSlot(slot1)
}
// Duplicate the top operand stack value and insert two or three values down
type DUP_X2 struct{ base.NoOperandsInstruction }
/*
bottom -> top
[...][c][b][a]
_____/
|
V
[...][a][c][b][a]
*/
func (self *DUP_X2) Execute(frame *rtda.Frame) {
stack := frame.OperandStack()
slot1 := stack.PopSlot()
slot2 := stack.PopSlot()
slot3 := stack.PopSlot()
stack.PushSlot(slot1)
stack.PushSlot(slot3)
stack.PushSlot(slot2)
stack.PushSlot(slot1)
}
// Duplicate the top one or two operand stack values
type DUP2 struct{ base.NoOperandsInstruction }
/*
bottom -> top
[...][c][b][a]____
\____ |
| |
V V
[...][c][b][a][b][a]
*/
func (self *DUP2) Execute(frame *rtda.Frame) {
stack := frame.OperandStack()
slot1 := stack.PopSlot()
slot2 := stack.PopSlot()
stack.PushSlot(slot2)
stack.PushSlot(slot1)
stack.PushSlot(slot2)
stack.PushSlot(slot1)
}
// Duplicate the top one or two operand stack values and insert two or three values down
type DUP2_X1 struct{ base.NoOperandsInstruction }
/*
bottom -> top
[...][c][b][a]
_/ __/
| |
V V
[...][b][a][c][b][a]
*/
func (self *DUP2_X1) Execute(frame *rtda.Frame) {
stack := frame.OperandStack()
slot1 := stack.PopSlot()
slot2 := stack.PopSlot()
slot3 := stack.PopSlot()
stack.PushSlot(slot2)
stack.PushSlot(slot1)
stack.PushSlot(slot3)
stack.PushSlot(slot2)
stack.PushSlot(slot1)
}
// Duplicate the top one or two operand stack values and insert two, three, or four values down
type DUP2_X2 struct{ base.NoOperandsInstruction }
/*
bottom -> top
[...][d][c][b][a]
____/ __/
| __/
V V
[...][b][a][d][c][b][a]
*/
func (self *DUP2_X2) Execute(frame *rtda.Frame) {
stack := frame.OperandStack()
slot1 := stack.PopSlot()
slot2 := stack.PopSlot()
slot3 := stack.PopSlot()
slot4 := stack.PopSlot()
stack.PushSlot(slot2)
stack.PushSlot(slot1)
stack.PushSlot(slot4)
stack.PushSlot(slot3)
stack.PushSlot(slot2)
stack.PushSlot(slot1)
}
代码解释
dup指令复制栈顶的单个变量。

dup_x1指令复制栈顶的单个变量并插入栈顶2个Slot下面。

其他几条指令大致差不多,不在阐述。
swap指令
整体代码
在instructions\stack目录下创建swap.go文件,在其中定义swap指令,代码如下:
package stack
import (
"jvmgo/ch05/instructions/base"
"jvmgo/ch05/rtda"
)
// Swap the top two operand stack values
type SWAP struct{ base.NoOperandsInstruction }
/*
bottom -> top
[...][c][b][a]
\/
/\
V V
[...][c][a][b]
*/
func (self *SWAP) Execute(frame *rtda.Frame) {
stack := frame.OperandStack()
slot1 := stack.PopSlot()
slot2 := stack.PopSlot()
stack.PushSlot(slot1)
stack.PushSlot(slot2)
}
代码解释:
swap指令交换栈顶的两个变量,Execute()方法如下:

代码太简单,不在阐述。
3.参考
尚硅谷宋红康:JVM全套教程:https://www.bilibili.com/video/BV1PJ411n7xZ
周志明:深入理解java虚拟机
张秀宏:自己动手写Java虚拟机 (Java核心技术系列)


