JVM笔记----从JVM角度浅谈 i++ 和 ++i 的区别
我先引入4个问题:
//第一类问题 int i1 = 10; i1++; int i2 = 10; i2++; //第二类问题 int i3 = 10; int i4 = i3++; int i5 = 10; int i6 = ++i5; //第三类问题 int i7 = 10; i7 = i7 ++; int i8 = 10; i8 = ++i8; //第四类问题 int i9 = 10; int i10 = i9++ + ++i9;
对于i++ 和 ++i相信大家在学习C语言都时候都很好的区别了它们,那这两个玩意在JVM中又是怎么回事呢。
如果不从JVM的角度来看 ++ 运算符相对来说比较简单看,因为只要看运算符优先级就好了。
只从运算符优先级来看的话1-3类问题都是小儿科,也就是第4类问题绕一点。
//第四类问题 int i9 = 10; int i10 = i9++ + ++i9;
我们来看一下,根据运算符优先级,表达式从左向右运行,首先是 i9++ ,这里 i9 先带入计算 然后自加,然后加上 ++ i9,对于这里是i9先自加再带入运算,
所以就是10 + 12 ,答案就是22,运算结果也是这样的。
但是如果我们要从JVM角度来看的话这个问题应该怎么看呢。
首先我们先从JVM的运行时数据区来看,
我们这里主要看虚拟机栈,不同的线程又有不同的虚拟机栈,由于我们这里只有一个线程,所以线程问题不用考虑了,main函数所在的线程栈只有一个栈帧,
也就是这里的main函数,而栈帧又是由局部变量表,操作数栈,动态链接,方法返回地址,附加信息组成。这里我们只要关心局部变量表和操作数栈就好了。
以下为了方便,我就把局部变量表简称为表,操作数栈简称为栈了。
我们首先来看一下这个程序的字节码:
0 bipush 10 2 istore_1 3 iinc 1 by 1 6 bipush 10 8 istore_2 9 iinc 2 by 1 12 bipush 10 14 istore_3 15 iload_3 16 iinc 3 by 1 19 istore 4 21 bipush 10 23 istore 5 25 iinc 5 by 1 28 iload 5 30 istore 6 32 bipush 10 34 istore 7 36 iload 7 38 iinc 7 by 1 41 istore 7 43 bipush 10 45 istore 8 47 iinc 8 by 1 50 iload 8 52 istore 8 54 bipush 10 56 istore 9 58 iload 9 60 iinc 9 by 1 63 iinc 9 by 1 66 iload 9 68 iadd 69 istore 10 71 return
我们先来关注第一类问题:
int i1 = 10; i1++; int i2 = 10; i2++;
它的字节码是:
0 bipush 10 2 istore_1 3 iinc 1 by 1 6 bipush 10 8 istore_2 9 iinc 2 by 1
它的操作顺序是这样的:
首先把10压入栈中,再把栈顶元素从10存入到表中slot[1]位置 ( 因为slot[0] 是args参数 )
而 iinc 1 by 1 这行代码是指把表中下标为1的数加上1。
所以这就是 int i1 = 10 ; i1 ++ ; 的字节码细节,i2操作同理。
我首先来说明一下这里的常用字节码指令:
iload : 从表 -> 栈 istore : 从栈 -> 表 ipush : 常量 -> 栈 iinc x by n : slot[x](表中下标为x的数据) += n
再来看一下第二类问题:
int i3 = 10; int i4 = i3++; int i5 = 10; int i6 = ++i5;
它的字节码是:
12 bipush 10 14 istore_3 15 iload_3 16 iinc 3 by 1 19 istore 4
21 bipush 10 23 istore 5 25 iinc 5 by 1 28 iload 5 30 istore 6
其中第12 到 19 是前两行操作,先把10压入栈中,在把10弹栈存入表中下标为3的位置,在从表中取出下标为3的数据压入栈中,表中下标为3的数据自加1,然后再把栈顶元素存入到表中下标为4的位置。所以最后i3 = 11,i4 = 10;
而下两行java程序与刚刚这两行唯一的区别就是iload 和 iinc by 的顺序反了。
如果先执行iload则是把表中数据压入栈中,由于我们执行iinc by操作只会对表中数据产生影响,而最终istore操作是把栈中的数存放到表中,所以iinc by操作对我们istore位置的数没有影响。
而后执行iload则不同了,我们先执行iinc by操作先把表中数据自加了,再从表压入栈中,那么最终自加的数据就会保留。
好了,i++ 和 ++i区别的精髓就在这里了,第三类问题和第四类问题交给你们解释了~