jvm位元組碼解析i++和++
阿新 • • 發佈:2018-11-26
i++和++i是編碼中比較常用的程式碼,並且也是初學者容易混淆的。我們知道i++是先賦值再+自己,而++i是先+自己再賦值。為什麼是這樣呢?他們之間效率對比又是怎麼樣呢?本文我們從位元組碼層面來分析。
前面的文章介紹過了jvm位元組碼的基本知識還有如何分析位元組碼,我們現在還是寫個小demo
public class TestSelfAdd { public static void f1() { int i=0; int j = i++; System.out.println(j); } public static void f2() { int i=0; int j = ++i; System.out.println(j); } }
編譯成位元組碼之後:(如何編譯成位元組碼看上一篇文章)
Classfile /G:/WorkingProjects/learn/project/test/out/production/test/TestSelfAdd.class Last modified 2018-11-19; size 580 bytes MD5 checksum fc262482f3054fd5f4c2bb18edf2355a Compiled from "TestSelfAdd.java" public class TestSelfAdd minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #5.#20 // java/lang/Object."<init>":()V #2 = Fieldref #21.#22 // java/lang/System.out:Ljava/io/PrintStream; #3 = Methodref #23.#24 // java/io/PrintStream.println:(I)V #4 = Class #25 // TestSelfAdd #5 = Class #26 // java/lang/Object #6 = Utf8 <init> #7 = Utf8 ()V #8 = Utf8 Code #9 = Utf8 LineNumberTable #10 = Utf8 LocalVariableTable #11 = Utf8 this #12 = Utf8 LTestSelfAdd; #13 = Utf8 f1 #14 = Utf8 i #15 = Utf8 I #16 = Utf8 j #17 = Utf8 f2 #18 = Utf8 SourceFile #19 = Utf8 TestSelfAdd.java #20 = NameAndType #6:#7 // "<init>":()V #21 = Class #27 // java/lang/System #22 = NameAndType #28:#29 // out:Ljava/io/PrintStream; #23 = Class #30 // java/io/PrintStream #24 = NameAndType #31:#32 // println:(I)V #25 = Utf8 TestSelfAdd #26 = Utf8 java/lang/Object #27 = Utf8 java/lang/System #28 = Utf8 out #29 = Utf8 Ljava/io/PrintStream; #30 = Utf8 java/io/PrintStream #31 = Utf8 println #32 = Utf8 (I)V { public TestSelfAdd(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 1: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this LTestSelfAdd; public static void f1(); descriptor: ()V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=2, args_size=0 0: iconst_0 1: istore_0 2: iload_0 3: iinc 0, 1 6: istore_1 7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 10: iload_1 11: invokevirtual #3 // Method java/io/PrintStream.println:(I)V 14: return LineNumberTable: line 3: 0 line 4: 2 line 5: 7 line 6: 14 LocalVariableTable: Start Length Slot Name Signature 2 13 0 i I 7 8 1 j I public static void f2(); descriptor: ()V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=2, args_size=0 0: iconst_0 1: istore_0 2: iinc 0, 1 5: iload_0 6: istore_1 7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 10: iload_1 11: invokevirtual #3 // Method java/io/PrintStream.println:(I)V 14: return LineNumberTable: line 8: 0 line 9: 2 line 10: 7 line 11: 14 LocalVariableTable: Start Length Slot Name Signature 2 13 0 i I 7 8 1 j I } SourceFile: "TestSelfAdd.java"
我們抽取了他們關鍵的部分:
i++ ++i 0: iconst_0 0: iconst_0 1: istore_0 1: istore_0 2: iload_0 2: iinc 0, 1 3: iinc 0, 1 5: iload_0 6: istore_1 6: istore_1 10: iload_1 10: iload_1
我們仔細分析,最後iload_1賦值輸出的是本地變量表的索引為1的元素,本地變量表那個元素的來源是棧,棧的元素來源又是本地變量表,iinc 0,1的意思是本地變量表索引為0的元素增加1。在棧的元素更新之前,也就是之心iload_0之前,++i的程式碼已經iinc過了,而i++的沒有,所以最終iload_1之後,i++結果為0,而++i為1。
分析完i++和++i的原理,我們思考一個問題,他們誰的效率高?
同樣我們寫個demo:
public class TestSelfAddEfficiency {
public static void f1() {
for(int i=0;i<10;i++) {
System.out.println(i);
}
}
public static void f2() {
for(int i=0;i<10;++i) {
System.out.println(i);
}
}
}
編譯之後看位元組碼,可以看到兩個函式他們的位元組碼是一樣的,所以效率上沒有差別
Classfile /G:/WorkingProjects/learn/project/test/out/production/test/TestSelfAddEfficiency.class
Last modified 2018-11-19; size 646 bytes
MD5 checksum a78a263c0750a489f5999bc1a6905ad2
Compiled from "TestSelfAddEfficiency.java"
public class TestSelfAddEfficiency
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #5.#20 // java/lang/Object."<init>":()V
#2 = Fieldref #21.#22 // java/lang/System.out:Ljava/io/PrintStream;
#3 = Methodref #23.#24 // java/io/PrintStream.println:(I)V
#4 = Class #25 // TestSelfAddEfficiency
#5 = Class #26 // java/lang/Object
#6 = Utf8 <init>
#7 = Utf8 ()V
#8 = Utf8 Code
#9 = Utf8 LineNumberTable
#10 = Utf8 LocalVariableTable
#11 = Utf8 this
#12 = Utf8 LTestSelfAddEfficiency;
#13 = Utf8 f1
#14 = Utf8 i
#15 = Utf8 I
#16 = Utf8 StackMapTable
#17 = Utf8 f2
#18 = Utf8 SourceFile
#19 = Utf8 TestSelfAddEfficiency.java
#20 = NameAndType #6:#7 // "<init>":()V
#21 = Class #27 // java/lang/System
#22 = NameAndType #28:#29 // out:Ljava/io/PrintStream;
#23 = Class #30 // java/io/PrintStream
#24 = NameAndType #31:#32 // println:(I)V
#25 = Utf8 TestSelfAddEfficiency
#26 = Utf8 java/lang/Object
#27 = Utf8 java/lang/System
#28 = Utf8 out
#29 = Utf8 Ljava/io/PrintStream;
#30 = Utf8 java/io/PrintStream
#31 = Utf8 println
#32 = Utf8 (I)V
{
public TestSelfAddEfficiency();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 1: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this LTestSelfAddEfficiency;
public static void f1();
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=0
0: iconst_0
1: istore_0
2: iload_0
3: bipush 10
5: if_icmpge 21
8: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
11: iload_0
12: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
15: iinc 0, 1
18: goto 2
21: return
LineNumberTable:
line 3: 0
line 4: 8
line 3: 15
line 6: 21
LocalVariableTable:
Start Length Slot Name Signature
2 19 0 i I
StackMapTable: number_of_entries = 2
frame_type = 252 /* append */
offset_delta = 2
locals = [ int ]
frame_type = 250 /* chop */
offset_delta = 18
public static void f2();
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=0
0: iconst_0
1: istore_0
2: iload_0
3: bipush 10
5: if_icmpge 21
8: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
11: iload_0
12: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
15: iinc 0, 1
18: goto 2
21: return
LineNumberTable:
line 8: 0
line 9: 8
line 8: 15
line 11: 21
LocalVariableTable:
Start Length Slot Name Signature
2 19 0 i I
StackMapTable: number_of_entries = 2
frame_type = 252 /* append */
offset_delta = 2
locals = [ int ]
frame_type = 250 /* chop */
offset_delta = 18
}
SourceFile: "TestSelfAddEfficiency.java"