从i=i++开始的JVM之旅
JVM分类建了很久了,一直都没写,总感觉这方面太头疼,一直都似懂非懂。
今天做到了一道题目 关于i=i++,方法间参数的传递
其实题目也没这么难,也就是i=i++,i的结果的问题。还有方法间参数的传递。
已经写了很久这种东西,基本上没去细细想为什么。反正很自然的做对了。但是看评论还是看到了一个大神通过javap从栈帧的角度去解释了答案,突然发现我能看懂他从JVM的角度去解释答案的过程。
才感觉其实我深入JVM也看了很久了。还是有点体会的。第一次从JVM的角度去思考问题(以前都是似懂非懂,看勉强可以,但是解释是说不清楚道理的,也记不住),有种量变到质变的感觉。所以决定也开始在JVM上写博客了。
加油!
介绍
i++和++i其实到深点就是JVM解释执行引擎的问题。 也就是字节码指令顺序的问题。下面对多个常见操作
i=i++,i++,i=++i,i=i+1来说一下对基于操作数栈的执行引擎
j=j++。
javap:
图示逐行注释:
首先要知道的是栈帧的意思。是虚拟机进行方法调用的和方法执行的数据结构。栈振包含局部变量表,操作数栈和栈帧信息。这里只需要关注局部变量表和操作数栈。(画图能力有限了。。)
一个重要的概念就是Java的是基于栈(操作数栈)的执行引擎。基本上所有的操作都是基于操作数栈。i++和++i自增这类操作比较特殊,是对局部变量表的值进行操作。但是也很好记忆。
好了,开始介绍了。(这里用了2个变量,方便理解局部变量表index)
1 int i=1; //这个操作的过程(01)就是int值1入栈,然后出栈赋值给局部变量表的i。是栈对1这个元素的入栈出栈操作。。
2 int j =2 //一样(23),栈对2这个元素的入栈出栈操作。
3 j = j++; // 这里看到操作过程(456)先拷贝压栈(副本的j=2),然后局部变量表的j+1;最后局部变量的栈顶值出栈赋值给局部变量表。所以局部变量表的j是等于2的。
j++的情况。
这里应该可以理解到了自增的字节码指令就是iinc,而j=j++多的2步操作是什么呢。就是一个赋值的操作。上面说了是基于栈的操作。所以赋值也要入栈出栈。而在j++中iinc自增优先级是低于iload拷贝入栈的。所以拷贝入栈,自增,然后拷贝的副本出栈赋值。
但是为什么j++打印的结果是3,而j=j++是2呢。
这还是要回到基于栈的操作。要打印j的值,就要把局部变量表的j拷贝入栈,然后打印的方法对栈顶的j进行操作。j++是iinc操作,局部变量表的j+1,而j=j+1操作过后,j其实还是原来的值。
所以结果就不一样啦。
++j
对比上面的j=j++操作可以得知。++j中iinc自增优先级是高于于iload拷贝入栈的。所以自增,拷贝,然后拷贝的副本出栈赋值。
j=j+1;
结合前面的介绍这里应该比较容易理解了。要对j进行操作,就要
4:先拷贝j入栈
5:int常量值1入栈,
6:iadd,最靠近栈顶的2个int型元素相加后结果入栈。
7:栈顶元素出栈,值赋给j。
总结:
总结:从局部变量表和操作数栈的结构,以及基于栈的解释执行引擎的方向去思考的话,应该会对这个自增过程有更深刻的理解。当然方法间参数的传递也可以用栈帧的特性来解释,后面会进行用题目来解释.
参考:<深入Java虚拟机>