背景
java 虚拟机支持的字节码指令数超过 200。大多数字节码指令所做的事情是很直观的(例如用于执行算术运算的指令)。如果可以看到指令的执行效果,也许能帮助我们理解对应的指令。我们先从几个简单的指令入手,用图形化界面展示它们的执行效果。本文会提供代码,展示以下指令的执行效果
- iadd
- isub
- iconst_<i> 这是一组指令,它包含以下 7 个指令
- iconst_m1
- iconst_0
- iconst_1
- iconst_2
- iconst_3
- iconst_4
- iconst_5
正文
指令介绍
iadd 指令
iadd 页面介绍了 iadd 指令的作用 ⬇️

这个指令会对操作数栈 (Operand Stack)栈顶的两个元素执行出栈操作,再对这两个元素的和执行入栈操作。
isub 指令
isub 页面介绍了 isub 指令的作用 ⬇️

这个指令会对操作数栈 (Operand Stack)栈顶的两个元素执行出栈操作,再对这两个元素的差(原本更接近栈底 的元素是 value1,另一个元素是 value2,那么差值为 value1−value2)执行入栈操作。
iconst_<i> 指令
iconst_<i> 是一组指令,它包含以下 7 个指令
- iconst_m1
- iconst_0
- iconst_1
- iconst_2
- iconst_3
- iconst_4
- iconst_5
iconst_<i> 页面介绍了这些指令的作用 ⬇️

这些指令会对操作数栈 (Operand Stack)执行入栈操作(入栈的值是 −1,0,1,2,3,4,5 中的某一个)。
代码实战
我们已经有了足够的理论知识,现在可以写代码进行实战了。我是先找豆包,帮我生成了初版的代码,然后在 trae 的协助下,又做了不少调整,最终的代码是这样的 ⬇️ (请将代码保存为 OperandStackVisualizer.py)
python3
import tkinter as tk
from tkinter import ttk, messagebox
class OperandStackVisualizer:
def __init__(self, root):
self.root = root
self.root.title("Operand Stack Visualizationizer")
self.stack = [] # 栈数据
# 界面样式
self.frame = ttk.Frame(root, padding=20)
self.frame.pack()
# 画布:用来画栈的方块
self.height = 500
self.canvas = tk.Canvas(self.frame, width=300, height=self.height, bg="white")
self.canvas.grid(row=0, column=0, columnspan=3, pady=10)
# 按钮
ttk.Button(self.frame, text="iconst_m1", command=lambda: self.push(-1)).grid(row=1, column=0, padx=5)
ttk.Button(self.frame, text="iconst_0", command=lambda: self.push(0)).grid(row=1, column=1, padx=5)
ttk.Button(self.frame, text="iconst_1", command=lambda: self.push(1)).grid(row=1, column=2, padx=5)
ttk.Button(self.frame, text="iconst_2", command=lambda: self.push(2)).grid(row=2, column=0, padx=5)
ttk.Button(self.frame, text="iconst_3", command=lambda: self.push(3)).grid(row=2, column=1, padx=5)
ttk.Button(self.frame, text="iconst_4", command=lambda: self.push(4)).grid(row=2, column=2, padx=5)
ttk.Button(self.frame, text="iconst_5", command=lambda: self.push(5)).grid(row=3, column=0, padx=5)
ttk.Button(self.frame, text="iadd", command=self.add).grid(row=3, column=1, padx=5)
ttk.Button(self.frame, text="isub", command=self.sub).grid(row=3, column=2, padx=5)
# 初始绘制
self.update_stack()
# ------------------------ 栈操作 ------------------------
def push(self, value):
self.stack.append(value)
self.update_stack()
def add(self):
if (len(self.stack) < 2):
messagebox.showwarning("警告", "栈中元素不足2个,无法执行加法操作!")
return
value2 = self.stack.pop()
value1 = self.stack.pop()
self.stack.append(value1 + value2)
self.update_stack()
def sub(self):
if (len(self.stack) < 2):
messagebox.showwarning("警告", "栈中元素不足2个,无法执行减法操作!")
return
value2 = self.stack.pop()
value1 = self.stack.pop()
self.stack.append(value1 - value2)
self.update_stack()
# ------------------------ 图形化刷新栈 ------------------------
def update_stack(self):
self.canvas.delete("all")
block_width = 100
block_height = 50
x = 100
# 从下往上画栈(最下面是栈底)
for i, val in enumerate(self.stack):
y = self.height - (i+1)*block_height - 10
self.canvas.create_rectangle(x, y, x+block_width, y+block_height,
fill="#87CEEB", outline="black", width=2)
self.canvas.create_text(x+block_width/2, y+block_height/2, text=val)
# ------------------------ 启动程序 ------------------------
if __name__ == "__main__":
root = tk.Tk()
app = OperandStackVisualizer(root)
root.mainloop()
使用如下命令可以运行
bash
python3 OperandStackVisualizer.py
最开始看到的是空的操作数栈 ⬇️

我们可以通过点击以下按钮来向栈中添加元素
iconst_m1iconst_0iconst_1iconst_2iconst_3iconst_4iconst_5
我随便添加了几个元素 ⬇️

此时如果点击 iadd 按钮,栈顶的 3 和 4 就会出栈,它们的和(即, 7)会入栈 ⬇️

此时如果点击 isub 按钮,栈顶的 7 和 2 就会出栈,它们的差(即, 2−7=−5)会入栈 ⬇️

参考资料
- The Java® Virtual Machine Specification 中
- 关于 iadd 指令的介绍
- 关于 isub 指令的介绍
- 关于 iconst_<i> 中各个指令的介绍(它包含了 7 个指令)