介绍
对于之前的for循环在jvm中如何实现
9. jvm9:字节码指令,我进行了思考分析,而对于我们日常开发却经常习惯写的是Foreach语句,对于Foreach语句的本质,我们需要深入了解一下。
数组中的Foreach
public class MyTest {
public static void main(String[] args) {
int[] arr = new int[10];
arr[0] = 1;
for (int i : arr) {
System.out.println(i);
}
}
}
javap -verbose MyTest.class查看:
C:\Users\Administrator\Desktop>javap -verbose MyTest.class
Classfile /C:/Users/Administrator/Desktop/MyTest.class
Last modified 2018-9-26; size 664 bytes
MD5 checksum 16772f3ceb324e67bfb14f4271c0fe3e
Compiled from "MyTest.java"
public class cn.spy.spring.test.MyTest
minor version: 0
major version: 51
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Class #2 // cn/spy/spring/test/MyTest
#2 = Utf8 cn/spy/spring/test/MyTest
#3 = Class #4 // java/lang/Object
#4 = Utf8 java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = Utf8 Code
#8 = Methodref #3.#9 // java/lang/Object."<init>":()V
#9 = NameAndType #5:#6 // "<init>":()V
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lcn/spy/spring/test/MyTest;
#14 = Utf8 main
#15 = Utf8 ([Ljava/lang/String;)V
#16 = Fieldref #17.#19 // java/lang/System.out:Ljava/io/Print
Stream;
#17 = Class #18 // java/lang/System
#18 = Utf8 java/lang/System
#19 = NameAndType #20:#21 // out:Ljava/io/PrintStream;
#20 = Utf8 out
#21 = Utf8 Ljava/io/PrintStream;
#22 = Methodref #23.#25 // java/io/PrintStream.println:(I)V
#23 = Class #24 // java/io/PrintStream
#24 = Utf8 java/io/PrintStream
#25 = NameAndType #26:#27 // println:(I)V
#26 = Utf8 println
#27 = Utf8 (I)V
#28 = Utf8 args
#29 = Utf8 [Ljava/lang/String;
#30 = Utf8 arr
#31 = Utf8 [I
#32 = Utf8 i
#33 = Utf8 I
#34 = Utf8 StackMapTable
#35 = Class #29 // "[Ljava/lang/String;"
#36 = Class #31 // "[I"
#37 = Utf8 SourceFile
#38 = Utf8 MyTest.java
{
public cn.spy.spring.test.MyTest();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>
":()V
4: return
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcn/spy/spring/test/MyTest;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=6, args_size=1
0: bipush 10
2: newarray int
4: astore_1
5: aload_1
6: iconst_0
7: iconst_1
8: iastore
9: aload_1
10: dup
11: astore 5
13: arraylength
14: istore 4
16: iconst_0
17: istore_3
18: goto 36
21: aload 5
23: iload_3
24: iaload
25: istore_2
26: getstatic #16 // Field java/lang/System.out:Ljav
a/io/PrintStream;
29: iload_2
30: invokevirtual #22 // Method java/io/PrintStream.prin
tln:(I)V
33: iinc 3, 1
36: iload_3
37: iload 4
39: if_icmplt 21
42: return
LineNumberTable:
line 7: 0
line 8: 5
line 10: 9
line 11: 26
line 10: 33
line 13: 42
LocalVariableTable:
Start Length Slot Name Signature
0 43 0 args [Ljava/lang/String;
5 38 1 arr [I
26 7 2 i I
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 21
locals = [ class "[Ljava/lang/String;", class "[I", top, int, int, cla
ss "[I" ]
stack = []
frame_type = 14 /* same */
}
SourceFile: "MyTest.java"
说明:
可以看到jvm使用了命令if_icmplt,这是一个比较、跳转结合的命令来实现我们对于数组的Foreach命令。譬如arraylength这种命令就是获取数组的引用,jvm会获取数组的长度,然后将数组的长度值压入操作数栈,后面通过if_icmplt比较栈顶递增的i值(iinc)和栈顶数组长度值,可以看出和for循环的下标遍历无区别。
ArrayList中的Foreach
public class ForeachTest {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("0");
for (String str : list) {
System.out.println(str);
}
}
}
javap -verbose ForeachTest.class查看:
C:\Users\Administrator\Desktop>javap -verbose ForeachTest.class
Classfile /C:/Users/Administrator/Desktop/ForeachTest.class
Last modified 2018-9-26; size 1073 bytes
MD5 checksum e3e830f354ea6bec0dd621a954ffc2c9
Compiled from "ForeachTest.java"
public class cn.spy.spring.test.ForeachTest
minor version: 0
major version: 51
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Class #2 // cn/spy/spring/test/ForeachTest
#2 = Utf8 cn/spy/spring/test/ForeachTest
#3 = Class #4 // java/lang/Object
#4 = Utf8 java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = Utf8 Code
#8 = Methodref #3.#9 // java/lang/Object."<init>":()V
#9 = NameAndType #5:#6 // "<init>":()V
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lcn/spy/spring/test/ForeachTest;
#14 = Utf8 main
#15 = Utf8 ([Ljava/lang/String;)V
#16 = Class #17 // java/util/ArrayList
#17 = Utf8 java/util/ArrayList
#18 = Methodref #16.#9 // java/util/ArrayList."<init>":()V
#19 = String #20 // 0
#20 = Utf8 0
#21 = InterfaceMethodref #22.#24 // java/util/List.add:(Ljava/lang/Obje
ct;)Z
#22 = Class #23 // java/util/List
#23 = Utf8 java/util/List
#24 = NameAndType #25:#26 // add:(Ljava/lang/Object;)Z
#25 = Utf8 add
#26 = Utf8 (Ljava/lang/Object;)Z
#27 = InterfaceMethodref #22.#28 // java/util/List.iterator:()Ljava/uti
l/Iterator;
#28 = NameAndType #29:#30 // iterator:()Ljava/util/Iterator;
#29 = Utf8 iterator
#30 = Utf8 ()Ljava/util/Iterator;
#31 = InterfaceMethodref #32.#34 // java/util/Iterator.next:()Ljava/lan
g/Object;
#32 = Class #33 // java/util/Iterator
#33 = Utf8 java/util/Iterator
#34 = NameAndType #35:#36 // next:()Ljava/lang/Object;
#35 = Utf8 next
#36 = Utf8 ()Ljava/lang/Object;
#37 = Class #38 // java/lang/String
#38 = Utf8 java/lang/String
#39 = Fieldref #40.#42 // java/lang/System.out:Ljava/io/Print
Stream;
#40 = Class #41 // java/lang/System
#41 = Utf8 java/lang/System
#42 = NameAndType #43:#44 // out:Ljava/io/PrintStream;
#43 = Utf8 out
#44 = Utf8 Ljava/io/PrintStream;
#45 = Methodref #46.#48 // java/io/PrintStream.println:(Ljava/
lang/String;)V
#46 = Class #47 // java/io/PrintStream
#47 = Utf8 java/io/PrintStream
#48 = NameAndType #49:#50 // println:(Ljava/lang/String;)V
#49 = Utf8 println
#50 = Utf8 (Ljava/lang/String;)V
#51 = InterfaceMethodref #32.#52 // java/util/Iterator.hasNext:()Z
#52 = NameAndType #53:#54 // hasNext:()Z
#53 = Utf8 hasNext
#54 = Utf8 ()Z
#55 = Utf8 args
#56 = Utf8 [Ljava/lang/String;
#57 = Utf8 list
#58 = Utf8 Ljava/util/List;
#59 = Utf8 str
#60 = Utf8 Ljava/lang/String;
#61 = Utf8 LocalVariableTypeTable
#62 = Utf8 Ljava/util/List<Ljava/lang/String;>;
#63 = Utf8 StackMapTable
#64 = Class #56 // "[Ljava/lang/String;"
#65 = Utf8 SourceFile
#66 = Utf8 ForeachTest.java
{
public cn.spy.spring.test.ForeachTest();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>
":()V
4: return
LineNumberTable:
line 6: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcn/spy/spring/test/ForeachTest;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=1
0: new #16 // class java/util/ArrayList
3: dup
4: invokespecial #18 // Method java/util/ArrayList."<in
it>":()V
7: astore_1
8: aload_1
9: ldc #19 // String 0
11: invokeinterface #21, 2 // InterfaceMethod java/util/List.
add:(Ljava/lang/Object;)Z
16: pop
17: aload_1
18: invokeinterface #27, 1 // InterfaceMethod java/util/List.
iterator:()Ljava/util/Iterator;
23: astore_3
24: goto 44
27: aload_3
28: invokeinterface #31, 1 // InterfaceMethod java/util/Itera
tor.next:()Ljava/lang/Object;
33: checkcast #37 // class java/lang/String
36: astore_2
37: getstatic #39 // Field java/lang/System.out:Ljav
a/io/PrintStream;
40: aload_2
41: invokevirtual #45 // Method java/io/PrintStream.prin
tln:(Ljava/lang/String;)V
44: aload_3
45: invokeinterface #51, 1 // InterfaceMethod java/util/Itera
tor.hasNext:()Z
50: ifne 27
53: return
LineNumberTable:
line 9: 0
line 10: 8
line 12: 17
line 13: 37
line 12: 44
line 15: 53
LocalVariableTable:
Start Length Slot Name Signature
0 54 0 args [Ljava/lang/String;
8 46 1 list Ljava/util/List;
37 7 2 str Ljava/lang/String;
LocalVariableTypeTable:
Start Length Slot Name Signature
8 46 1 list Ljava/util/List<Ljava/lang/String;>;
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 27
locals = [ class "[Ljava/lang/String;", class java/util/List, top, cla
ss java/util/Iterator ]
stack = []
frame_type = 16 /* same */
}
SourceFile: "ForeachTest.java"
说明:
jvm在ArrayList的遍历上没有使用iinc自增下标和求数组长度在栈顶比较跳转,而是采用了invokeinterface命令调用Iterator接口的hasNext方法进行判断,使用ifne命令进行比较不等于0就进行跳转。
总结
1. 数组中的Foreach和下标的for循环没区别,都是采用自增下标和数组长度比较循环的。
2. ArrayList集合中的Foreach是采用Iterator接口的hasNext方法来实现判断的,ArrayList中的内部类通过实现Iterator接口,来实现对集合ArrayList的迭代,而jvm具体是通过ifne命令来判断不等于0则跳转继续循环。
网友评论