JVM字节码层面分析Java中的i++、++i
Java语言在JVM虚拟机上运行,不可避免的涉及到JVM指令集,下面我们简单分析几个案例。
在进行分析之前,先简单回顾一下JVM的运行时数据区中的JVM虚拟机栈。
一、JVM Stack - JVM虚拟机栈
JVM Stack描述的是:每个方法在执行的同时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从调用直至完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
默认情况下,一个线程对应一个栈,一个方法对应一个栈帧。

二、示例代码分析
Demo1:
public static void main(String[] args) { int i = 0; i = i++; System.out.println(i); }
输出结果是0。
Demo2:
public static void main(String[] args) { int i = 0; i = ++i; System.out.println(i); }
输出结果是1。
对于初学者,往往会搞不明白为什么会出现这样的结果,下面我们通过JVM指令来分析这两个Demo。
1、Demo1的分析
1.1、Demo1的mian()方法的局部变量表

main() 方法的局部变量表中有两个变量,分别是序号为0的args、序号为1的 i。
1.2、Demo1的main()方法的字节码指令

0、iconst_0 :将常量0压入操作数栈,

1、istore_1 : 弹出操作数栈栈顶元素即常量0,存储到到局部变量表序号为1的位置即变量i中;
至此 int i = 0 操作完成。

2、iload_1: 第1个变量(变量值为0)压入操作数栈

3、iinc 1 by 1:是将局部变量1中的数自增1
4、istrore_1 : 弹出操作数栈栈顶元素即常量0,存储到到局部变量表序号为1的位置即变量i中;

5、getstatic #2 : 获取常量池中#2对应变量的引用(<java/lang/System.out : Ljava/io/PrintStream;> )压入操作数栈

6、iload_1:第1个变量(变量值为0)压入操作数栈栈顶

7、invokevirtual #3 : 调用常量池中#3对应的常量,即打印的方法( <java/io/PrintStream.println : (I)V>)
· 找到常量池 #3 项;
· 定位到方法区 java/io/PrintStream.println:(I)V 方法
· 生成新的printf() 栈帧(分配 locals、stack等)
· 传递参数i,执行新栈帧中的字节码
· 执行完毕,弹出栈帧

8、return 方法结束
mian() 栈帧出栈
2、Demo2的分析
2.1、Demo2的mian()方法的局部变量表与Demo1一致
2.2、Demo1的main()方法的字节码指令

0、iconst_0 :将常量0压入操作数栈

1、istore_1 : 弹出操作数栈栈顶元素即常量0,存储到到局部变量表序号为1的位置即变量i中;至此 int i = 0 操作完成

2、iinc 1 by 1:是将局部变量 i 中的数自增 1, 此时变量i = 1

3、iload_1: 第1个变量 i (变量值为1)压入操作数栈

4、istore_1 : 弹出操作数栈栈顶元素 1,存储到到局部变量表序号为1的位置即变量 i 中

5、getstatic #2 : 获取常量池中#2 对应变量的引用(<java/lang/System.out : Ljava/io/PrintStream;> )压入操作数栈
6、iload_1 : 第1个变量(变量值为1)压入操作数栈栈顶
7、invokevirtual #3 : 调用常量池中#3对应的常量,即打印的方法( <java/io/PrintStream.println : (I)V>)
8、return 方法结束
三、总结
运行在JVM的业务逻辑,最终都是在JVM运行时数据区,通过JVM指令集,处理一系列变量值操作完成的。

浙公网安备 33010602011771号