通过字节码理解自增运算
文/年容
今天碰到一个题,然后尹小凡、覃小善、彭大年等三人挺感兴趣,遂立了个flag要搞清楚一下,然后也算有了今年的第一篇博文了。
源码如下,最后i、j、k的值为多少?
package io.github.ryan.simple;
public class Increment {
public void inc() {
int i = 1, j, k;
i = i++;
j = i++;
k = i + ++i * i++;
}
}
通过IntelliJ的ASM Bytecode Outline插件获取到inc()方法字节码操作顺序如下:
MAXSTACK = 3 // 最大栈深为3
MAXLOCALS = 4 // 最大局部变量表长度为4
public inc()V
L0
LINENUMBER 5 // 执行后栈深为0,1#屉i为1
ICONST_1 // 常量1入栈
ISTORE 1 // 常量1出栈并装入1#屉
L1
LINENUMBER 6 // 执行后栈深为0,1#屉i为1
ILOAD 1 // 1#屉的值1入栈
IINC 1 1 // 1#屉的值1自增为2
ISTORE 1 // 栈顶值1出栈并装入1#屉
L2
LINENUMBER 7 // 执行后栈深为0,1#屉i为1,2#屉j为1
ILOAD 1 // 1#屉的值1入栈
IINC 1 1 // 1#屉的值1自增为2
ISTORE 2 // 栈顶值1出栈装入2#屉
L3
LINENUMBER 8 // 执行后栈深为0,局部变量表长为4
ILOAD 1 // 1#屉值2入栈,栈深为1
IINC 1 1 // 1#屉的值2自增为3
ILOAD 1 // 1#屉值3入栈,栈深为2
ILOAD 1 // 1#屉值3入栈,栈深为3
IINC 1 1 // 1#屉值3自增为4
IMUL // 栈顶两个值相乘 3 * 3,并将结果入栈,栈深为2
IADD // 栈顶两个值相加 9 + 2,并将结果入栈,栈深为1
ISTORE 3 // 栈顶值11出栈装入3#屉,栈深为0
L4
LINENUMBER 9
RETURN
几点说明:
- 局部变量表可以看成是一排有编号的抽屉(从0开始编号),用于存放方法参数和局部变量。奇怪的是,看起来它的最大长度应该为3才对,因为inc()方法没有参数,并且只有i、j、k三个局部变量,可为什么最大长度会是4呢?因为对于非静态方法,0#屉存放的是方法所属对象的引用
- L0、L1、L2等表示标签,L代表label
- 多数指令是对操作栈的数进行操作的,IINC指令则比较特别,它直接对局部变量表上的数进行操作
- i = 4,j = 1,k = 11