0. 前言
- 问题来源:
- 疑问:
中间变量缓存机制
? - 深入理解Java中间变量的缓存机制
看到这位大佬的测试用例,我简直惊呆了,于是决定自己测试一下,以便从JVM层面了解问题的根源
1. 测试用例:
public class Test02
{
public static void main(String[] args)
{
int i = 8;
i++;
System.out.println(i);
int j = 8;
j = j++;
System.out.println(j);
}
}
2. 代码执行结果
9
8
3. jclasslib 后得到的 main 函数部分的字节码指令
0 bipush 8
2 istore_1
3 iinc 1 by 1
6 getstatic #2 <java/lang/System.out>
9 iload_1
10 invokevirtual #3 <java/io/PrintStream.println>
13 bipush 8
15 istore_2
16 iload_2
17 iinc 2 by 1
20 istore_2
21 getstatic #2 <java/lang/System.out>
24 iload_2
25 invokevirtual #3 <java/io/PrintStream.println>
28 return
4. 指令说明:
局部变量表
① bipush
:常量入栈指令,int
取值范围(-128~127)
② istore_1
:出栈指令,将操作数栈中的数据保存到局部变量表中1
的位置
③ iload_1
:加载指令,将局部变量表中1
位置的数据加载到操作数栈中
5. 基于栈的解释器执行过程
i++
执行过程过程说明
程序计数器 | 指令 | 说明 |
---|---|---|
0: | bipush 8 | 将常量8保存到操作数栈中 |
2: | istore_1 | 取出操作数栈顶元素8放入局部变量表1的位置处 |
3: | iinc 1 by 1 | 在局部变量槽1的位置上自增 |
9: | iload_1 | 从局部变量表中加载1位置上的值到操作数栈顶 |
补充说明:3:
变量自增
① 在变量槽中自增的说法参考 从字节码理解Java中局部变量的自增/自减
② 执行3:
的时候,操作数栈为空,只有局部变量表中有值
j = j++
执行过程说明
程序计数器 | 指令 | 说明 |
---|---|---|
13: | bipush 8 | 将常量8保存到操作数栈中 |
15: | istore_2 | 取出操作数栈顶元素8放入局部变量表2的位置处 |
16: | iload_2 | 从局部变量表中加载2位置上的值到操作数栈顶 |
17: | iinc 2 by 1 | 在局部变量槽2的位置上自增 |
20: | istore_2 | 取出操作数栈顶元素8放入局部变量表2的位置处 |
24: | iload_2 | 从局部变量表中加载2位置上的值到操作数栈顶 |
上述执行过程梳理:
① 在执行17:
自增之前,将局部变量表中的数据保存到操作数栈中
② 在局部变量槽中执行自增操作
③ 操作数栈顶元素的值保存到局部变量表中,覆盖掉执行自增后的值
④ 从局部变量表中把被覆盖掉的值加载到操作栈上,以进行后续的输出操作
6. 总结:
两条语句的指令区别:
问题:
16:
:为什么要将数据保存到操作数栈中?
20:
:该行与赋值号=
之间的关联?