购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.3.5 for语句的字节码原理

纵观所有的字节码指令,并没有与for名字相关的指令,那for循环是如何实现的呢?接下来以sum相加求和的例子来看for循环的实现细节,代码如下所示。


public int sum(int[] numbers) {
    int sum = 0;
    for (int number : numbers) {
        sum += number;
    }
    return sum;
}

上面代码对应的字节码如下。


 0: iconst_0
 1: istore_2
 2: aload_1
 3: astore_3
 4: aload_3
 5: arraylength
 6: istore        4
 8: iconst_0
 9: istore        5
11: iload         5
13: iload         4
15: if_icmpge     35
18: aload_3
19: iload         5
21: iaload
22: istore        6
24: iload_2
25: iload         6
27: iadd
28: istore_2
29: iinc          5, 1
32: goto          11
35: iload_2
36: ireturn

为了方便理解,这里先把对应的局部变量表的示意图画出来,如图2-14所示。

图2-14 for循环局部变量表示意图

下面以numbers数组内容[10,20,30]为例来讲解上述的字节码的执行过程。

第0~1行:把常量0加载到操作数栈上,随后通过istore_2指令将0出栈赋值给局部变量表下标为2的元素,也就是给局部变量sum赋值为0,如图2-15所示。

图2-15 for循环执行细节(1)

第2~9行用来初始化循环控制变量,其伪代码如下所示。


$array = numbers;
$len = $array.arraylength
$i = 0

第2~3行:aload_1指令的作用是加载局部变量表中下标为1的变量(参数numbers),astore_3指令的作用是将栈顶元素存储到局部变量下标为3的位置上,记为$array,如图2-16所示。

图2-16 for循环执行细节(2)

第4~6行:计算数组的长度,astore_3加载$array到栈顶,调用arraylength指令获取数组长度存储到栈顶,随后调用istore 4将数组长度存储到局部变量表的第4个位置,这个变量是表示数组的长度值,记为$len,过程如图2-17所示。

第8~9行:初始化数组遍历的下标初始值。iconst_0将0加载到操作数栈上,随后istore 5将栈顶的0存储到局部变量表中的第5个位置,这个局部变量是数组遍历的下标初始值,记为$i,如图2-18所示。

11~32行是真正的循环体,详细介绍如下。

图2-17 for循环执行细节(3)

图2-18 for循环执行细节(4)

第11~15行的作用是判断循环能否继续。这部分的字节码如下所示。


11: iload         5
13: iload         4
15: if_icmpge     35

首先通过iload 5和iload 4指令加载$i和$len到栈顶,然后调用if_icmpge进行比较,如果$i>=$len,直接跳转到第35行指令处,for循环结束;如果$i<$len则继续往下执行循环体,可以用如下伪代码表示。


if ($i >= $len) goto 35;

过程如图2-19所示。

第18~22行的作用是把$array[$i]赋值给number。aload_3加载$array到栈上,iload 5加载$i到栈上,然后iaload指令把下标为$i的数组元素加载到操作数栈上,随后istore 6将栈顶元素存储到局部变量表下标为6的位置上,过程如图2-20所示。

图2-19 for循环执行细节(5)

图2-20 for循环执行细节(6)

第24~28行:iload_2和iload 6指令把sum和number值加载到操作数栈上,然后执行iadd指令进行整数相加,过程如图2-21所示。

第29行:“iinc 5,1”指令对执行循环后的$i加一。iinc指令比较特殊,之前介绍的指令都是基于操作数栈来实现功能,它则是直接对局部变量进行自增,不用先入栈、执行加一操作,再将结果出栈存储到局部变量表,因此效率非常高,适合循环结构,如图2-22所示。

第32行:goto 11指令的作用是跳转到第11行继续进行循环条件的判断。

图2-21 for循环执行细节(7)

图2-22 for循环执行细节(8)

上述字节码用伪代码表示就是:


@start: if ($i >= $len) return;
$item = $array[$i];
sum += $item;
++ $i
goto @start

整段代码的逻辑看起来非常熟悉,可以用下面的Java代码表示。


int sum = 0;
for (int i = 0; i < numbers.length; i++) {
    sum += numbers[i];
}
return sum;

由此可见,for(item:array)就是一个语法糖,字节码会让它现出原形,回归它的本质。 /LJ3nW1E4ifxTh6Zf1lXTCdgU7YTWSTMyibmu0fw4VChbASZWnXVi3eYr6k8Bbqh

点击中间区域
呼出菜单
上一章
目录
下一章
×