本文不對字節碼中的每一個指令進行解析,只對幾個實例進行解析,以此來了解java文件編譯後生產的class字節碼。實例中所有所有代碼均可直接使用,建議自己重新實驗一下。JVM字節碼在通常開發情況下沒有用處,但是是一種無侵入監控方法(動態字節碼技術)、匪夷所思的BUG調試的重要技術手段。
一、環境
(1)JDK1.7
(2)javac Test.java 生產Test.class
(3)javap -c Test.class > test.txt 輸出JVM字節碼
二、字節碼實例
字符串
StringAppend類爲例
源碼如下:
package com.owl.zookeeper.string;
public class StringAppend {
public static void main(String[] args) {
String a = "1";
String b = "2" + a + "3";
System.out.println(b);
}
}
字節碼如下: Compiled from "StringAppend.java"
public class com.owl.zookeeper.string.StringAppend {
public com.owl.zookeeper.string.StringAppend();
Code:
0: aload_0 //將this入棧
1: invokespecial #1 // Method java/lang/Object."<init>":()V //調用Object的構造方法
4: return //返回void
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String 1 解釋:LDC cst:將常量池偏移量爲cst的值入棧,譬如LDC #12,在操作棧中會佔用1個字長
2: astore_1 解釋:將棧頂的String 1賦值給第一個變量a
3: new #3 // class java/lang/StringBuilder 解釋:創建對象,並將該對象入棧頂
6: dup 解釋:複製棧頂數據StringBuilder。因爲方法調用會彈出參數(這裏是Object對象),因此需要上面的dup指令,保證在調用構造函數之後棧頂上還是 Object對象的引用,很多種情況下dup指令都是爲這個目的而存在的。
7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V 解釋:調用StringBuilder構造方法
10: ldc #5 // String 2 解釋:將String 2壓入棧頂
12: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 解釋:棧頂2出棧,作爲方法入參,調用java/lang/StringBuilder.append()方法
15: aload_1 解釋:將變量a的1入棧
16: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 解釋:將變量a的1出棧,作爲方法入參,調用java/lang/StringBuilder.append()方法
19: ldc #7 // String 3 解釋:將常量池中的字符串3入棧
21: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 解釋:將字符串3出棧,作爲方法入參,調用java/lang/StringBuilder.append()方法
24: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 解釋:調用StringBuilder.toString
27: astore_2 解釋:將返回值保存在變量b
28: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream; 解釋:獲得靜態變量,System.out
31: aload_2 解釋:變量2入棧
32: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 解釋:變量2出棧,作爲println的方法
35: return 解釋:返回
循環
StringForAppend類爲例
源碼如下:
public class StringForAppend {
public static void main(String[] args) {
String a = "";
for(int i = 0; i < 3; i ++) {
a += "1";
}
System.out.println(a);
}
}
字節碼如下:
Compiled from "StringForAppend.java"
public class com.owl.zookeeper.string.StringForAppend {
public com.owl.zookeeper.string.StringForAppend();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String 解釋:將常量空字符串入棧
2: astore_1 解釋:將空字符串賦值給a
3: iconst_0 解釋:將常量integer 0入棧
4: istore_2 解釋:出棧0,賦值給變量0
5: iload_2 解釋:將變量i=0,入棧
6: iconst_3 解釋:將integer 3入棧
7: if_icmpge 36 解釋:比較棧頂兩個元素,如果相等則跳轉到36
10: new #3 // class java/lang/StringBuilder 創建StringBuilder,併入棧
13: dup 解釋:複製棧頂
14: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V 解釋:出棧調用StringBuilder的構造方法
17: aload_1 解釋:入棧空字符串
18: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 出棧空字符串,出棧StringBuilder,執行append方法
21: ldc #6 // String 1 解釋:常量1入棧
23: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 出棧字符串1,調用append方法
26: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 出棧StringBuilder,調用append方法,將結果入棧
29: astore_1 解釋:出棧""+1,保存到變量a
30: iinc 2, 1 解釋:將變量i,遞增1
33: goto 5 解釋:跳轉到步驟5
36: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;解釋:獲得對象PrintStream,入棧
39: aload_1 解釋:入棧變量a
40: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 出棧變量a,出棧PrintStream,調用println方法
43: return 解釋:返回
判斷
StringIf類爲例
源碼如下:
public class StringIf {
public static void main(String[] args) {
String a = "123";
if("123" == (a)) {
System.out.println(a);
}else {
String b = a + "!";
System.out.println(b);
}
}
}
字節碼如下:
Compiled from "StringIf.java"
public class com.owl.zookeeper.string.StringIf {
public com.owl.zookeeper.string.StringIf();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String 123 解釋:將常量123入棧
2: astore_1 解釋:出棧123,賦值給變量a
3: ldc #2 // String 123 解釋:將常量123入棧
5: aload_1 解釋:入棧123
6: if_acmpne 19 解釋:比較棧頂兩個元素,如果符合跳轉到指定位置
9: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;解釋:獲得System.out對象,入棧
12: aload_1 解釋:入棧123
13: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 解釋:出棧參數,出棧調用方法對象
16: goto 46 解釋:跳轉到結束
19: new #5 // class java/lang/StringBuilder 創建一個 解釋:創建一個StringBuilder對象,併入棧
22: dup 解釋:複製棧頂數據,併入棧
23: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V 解釋:出棧並調用構造方法
26: aload_1 解釋:入棧123
27: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;解釋:出棧123,出棧StringBUilder,調用方法,入棧StirngBuilder
30: ldc #8 // String ! 解釋:入棧常量!
32: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;解釋:出棧!,出棧StringBuilder,調用append(!),入棧StirngBuilder
35: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 解釋:出棧StringBuilder,調用toString()
38: astore_2 解釋:出棧123!,賦值給變量b
39: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 解釋:獲得PrintStream,併入棧
42: aload_2 解釋:入棧123!
43: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 出棧2個棧位,調用println
46: return 解釋:返回