jvm之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_229: 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 行 : a l o a d 1 指 令 的 作 用 是 加 载 局 部 变 量 表 中 下 标 为 1 的 变 量 ( 参 数 n u m b e r s ) , a s t o r e 3 指 令 的 作 用 是 将 栈 顶 元 素 存 储 到 局 部 变 量 下 标 为 3 的 位 置 上 , 记 为 i = 0 第2~3行:aload_1指令的作用是加载局部变量表中下标为1的变量(参数numbers),astore_3指令的作用是将栈顶元素存储到局部变量下标为3的位置上,记为 i=023aload11numbersastore33array,如图2-16所示。
在这里插入图片描述
图2-16 
for循环执行细节(2)第4~6行:计算数组的长度,astore_3加载 a r r a y 到 栈 顶 , 调 用 a r r a y l e n g t h 指 令 获 取 数 组 长 度 存 储 到 栈 顶 , 随 后 调 用 i s t o r e 4 将 数 组 长 度 存 储 到 局 部 变 量 表 的 第 4 个 位 置 , 这 个 变 量 是 表 示 数 组 的 长 度 值 , 记 为 array到栈顶,调用arraylength指令获取数组长度存储到栈顶,随后调用istore 4将数组长度存储到局部变量表的第4个位置,这个变量是表示数组的长度值,记为 arrayarraylengthistore44len,过程如图2-17所示。
在这里插入图片描述
第8~9行:初始化数组遍历的下标初始值。iconst_0将0加载到操作数栈上,随后istore 5将栈顶的0存储到局部变量表中的第5个位置,这个局部变量是数组遍历的下标初始值,记为$i,如图2-18所示。11~32行是真正的循环体,详细介绍如下。for循环执行细节

(3)图2-18 for循环执行细节(4)第11~15行的作用是判断循环能否继续。这部分的字节码如下所示。11: iload 5
13: iload 4
15: if_icmpge 35
首先通过iload 5和iload 4指令加载 i 和 i和 ilen到栈顶,然后调用if_icmpge进行比较,如果 i > = i>= i>=len,直接跳转到第35行指令处,for循环结束;如果 i < i< i<len则继续往下执行循环体,可以用如下伪代码表示。if ($i >= l e n ) g o t o 35 ; 过 程 如 图 ! [ 在 这 里 插 入 图 片 描 述 ] ( h t t p s : / / i m g − b l o g . c s d n i m g . c n / 34 b 03320 d 04240 b e a c d 8 a b a b 7 c 6 a 5080. j p g ? x − o s s − p r o c e s s = i m a g e / w a t e r m a r k , t y p e d 3 F 5 L X p l b m h l a Q , s h a d o w 5 0 , t e x t Q 1 N E T i B A 5 L i N 54 i x 5 Y a Z 5 p a H 56 u g , s i z e 2 0 , c o l o r F F F F F F , t 7 0 , g s e , x 1 6 ) 。 第 18 ~ 22 行 的 作 用 是 把 len) goto 35; 过程如图 ![在这里插入图片描述](https://img-blog.csdnimg.cn/34b03320d04240beacd8abab7c6a5080.jpg?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5LiN54ix5YaZ5paH56ug,size_20,color_FFFFFF,t_70,g_se,x_16) 。第18~22行的作用是把 len)goto35;![](https://imgblog.csdnimg.cn/34b03320d04240beacd8abab7c6a5080.jpg?xossprocess=image/watermark,typed3F5LXplbmhlaQ,shadow50,textQ1NETiBA5LiN54ix5YaZ5paH56ug,size20,colorFFFFFF,t70,gse,x16)1822array[ i ] 赋 值 给 n u m b e r 。 a l o a d 3 加 载 i]赋值给number。aload_3加载 i]numberaload3array到栈上,iload 5加载 i 到 栈 上 , 然 后 i a l o a d 指 令 把 下 标 为 i到栈上,然后iaload指令把下标为 iialoadi的数组元素加载到操作数栈上,随后istore 6将栈顶元素存储到局部变量表下标为6的位置上,过程如图2-20所示。
在这里插入图片描述
图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 = a r r a y [ array[ 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)就是一个语法糖,字节码会让它现出原形,回归它的本质。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值