指令instruction:Java虚拟机指令由一个字节opcode组成,指定要执行的操作,然后是零或更多操作数,以提供操作使用的参数或数据。许多说明没有操作数,仅由opcode组成。(如iadd指令就是对栈顶的两个int值进行加法操作)[^1]
线程thread:每个线程都有自己的pc寄存器(Program Counter)和Java虚拟机栈(JVM Stack)。
虚拟机栈帧thread.stack:Java虚拟机栈又由栈帧(Stack Frame,简称帧)构成,帧中保存方法执行的状态,包括局部变量表(Local Variable)和操作数栈(Operand Stack)等。
操作数栈:thread.operandStack:可以将局部变量表的常量/引用加载(load)到栈上,计算后再存储(load)到局部变量表。[^2]

loop的时候,thread.currentFrame()调用的是topFrame


public class Test {
public static void main(String[] args) {
String [] y = new String []{"aa","bb"};
String r=new Test().test(y[0]);
System.out.println(y[1]);
}
public String test(String a){
int x=1+1;
return "abc";
}
}
编译javac Test.java 、 javap -c -v Test.class


当执行 String r=new Test().test(y[0]); ,主要是执行了 25: invokevirtual #7 // Method test:(Ljava/lang/String;)Ljava/lang/String; ,其指向常量池的#7,常量池中的 #7 指向了test方法。注:invokevirtual 调用对象的实例方法,在对象的(虚拟)类型上进行调度。[^2]
执行到第3步的时候,main这个栈帧(frame)会push一个新的栈帧到虚拟机栈。
这时候进入到loop方法,topFrame拿到最上面的(后进先出)test()方法栈。
进入该栈后,又有新的一系列指令,挨个执行test方法中的指令。
a. 因为不涉及调用其他方法,所以没有再新开栈帧,此时topFrame拿到的还是test()方法这个栈帧。
最后,test()方法对应的栈帧的末尾,执行areturn指令,areturn指令会popFrame(),下次循环时topFrame会回到调用test方法时对应的虚拟机栈帧。
E:\code\javap -v Test.class
Classfile /E:/code/Test.class
Last modified 2022年9月13日; size 591 bytes
MD5 checksum 0fa48c5ae00c667283eba96611d6c976
Compiled from "Test.java"
public class Test
minor version: 0
major version: 55
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #5 // Test
super_class: #11 // java/lang/Object
interfaces: 0, fields: 0, methods: 3, attributes: 1
Constant pool:
#1 = Methodref #11.#22 // java/lang/Object."":()V
#2 = Class #23 // java/lang/String
#3 = String #24 // aa
#4 = String #25 // bb
#5 = Class #26 // Test
#6 = Methodref #5.#22 // Test."":()V
#7 = Methodref #5.#27 // Test.test:(Ljava/lang/String;)Ljava/lang/String;
#8 = Fieldref #28.#29 // java/lang/System.out:Ljava/io/PrintStream;
#9 = Methodref #30.#31 // java/io/PrintStream.println:(Ljava/lang/String;)V
#10 = String #32 // abc
#11 = Class #33 // java/lang/Object
#12 = Utf8 <init>
#13 = Utf8 ()V
#14 = Utf8 Code
#15 = Utf8 LineNumberTable
#16 = Utf8 main
#17 = Utf8 ([Ljava/lang/String;)V
#18 = Utf8 test
#19 = Utf8 (Ljava/lang/String;)Ljava/lang/String;
#20 = Utf8 SourceFile
#21 = Utf8 Test.java
#22 = NameAndType #12:#13 // "":()V
#23 = Utf8 java/lang/String
#24 = Utf8 aa
#25 = Utf8 bb
#26 = Utf8 Test
#27 = NameAndType #18:#19 // test:(Ljava/lang/String;)Ljava/lang/String;
#28 = Class #34 // java/lang/System
#29 = NameAndType #35:#36 // out:Ljava/io/PrintStream;
#30 = Class #37 // java/io/PrintStream
#31 = NameAndType #38:#39 // println:(Ljava/lang/String;)V
#32 = Utf8 abc
#33 = Utf8 java/lang/Object
#34 = Utf8 java/lang/System
#35 = Utf8 out
#36 = Utf8 Ljava/io/PrintStream;
#37 = Utf8 java/io/PrintStream
#38 = Utf8 println
#39 = Utf8 (Ljava/lang/String;)V
{
public Test();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: return
LineNumberTable:
line 2: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=3, args_size=1
0: iconst_2
1: anewarray #2 // class java/lang/String
4: dup
5: iconst_0
6: ldc #3 // String aa
8: aastore
9: dup
10: iconst_1
11: ldc #4 // String bb
13: aastore
14: astore_1
15: new #5 // class Test
18: dup
19: invokespecial #6 // Method "":()V
22: aload_1
23: iconst_0
24: aaload
25: invokevirtual #7 // Method test:(Ljava/lang/String;)Ljava/lang/String;
28: astore_2
29: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
32: aload_1
33: iconst_1
34: aaload
35: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
38: return
LineNumberTable:
line 4: 0
line 5: 15
line 6: 29
line 7: 38
public java.lang.String test(java.lang.String);
descriptor: (Ljava/lang/String;)Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=3, args_size=2
0: iconst_2
1: istore_2
2: ldc #10 // String abc
4: areturn
LineNumberTable:
line 9: 0
line 10: 2
}
SourceFile: "Test.java"