天天看點

簡單了解Java位元組碼

java的“一處編譯、多處執行”,得益于它的位元組碼和JVM。位元組碼就是java編譯後的能被JVM讀懂的類彙編語言。

1.舉個例子:

package jvm;

public class A {
}
           

上面是一個最簡單的類,編譯該類:

javac A.java
           

生成對應的A.class檔案,反編譯該class:

javap A.class
Compiled from "A.java"
public class jvm.A {
  public jvm.A();
}
           

反編譯後,可以看到預設的構造函數jvm.A()。

如果要看到位元組碼,可以使用如下的指令:

javap -c A.class 
Compiled from "A.java"
public class jvm.A {
  public jvm.A();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return
}
           

簡單解釋一下:

aload_0 表示将第一個變量(引用類型)壓入堆棧,對于非靜态方法,第一個引用變量是this,即将this指針壓棧。aload_0的16進制代碼為0x2a

invokespecial 表示調用超類構造方法,其十六進制碼為0xb7

return 表示目前方法傳回void,其十六進制碼為0xb1

2.更複雜些的例子

package jvm;

public class A {
	public int add(int a, int b) {
        return a + b;
    }
}
           

更複雜些的類A,編譯再反編譯後,可以看到如下的位元組碼:

javap -c A.class 
Compiled from "A.java"
public class jvm.A {
  public jvm.A();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public int add(int, int);
    Code:
       0: iload_1
       1: iload_2
       2: iadd
       3: ireturn
}
           

預設的構造函數之外,增加了新的方法int add(int, int),它的位元組碼解釋如下:

iload_1 将第二個變量(int類型)壓棧

iload_2 将第三個變量(int類型)壓棧

iadd 将棧頂兩int型數值相加并将結果壓入棧頂

ireturn 傳回int類型

注意,之是以是iload_1而不是iload_0是因為目前是非靜态方法,第一個變量預設為this。如果上述add方法換為靜态方法,則反編譯後,位元組碼如下:

javap -c A.class 
Compiled from "A.java"
public class jvm.A {
  public jvm.A();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static int add(int, int);
    Code:
       0: iload_0
       1: iload_1
       2: iadd
       3: ireturn
}
           

後續文章還會接着本文的例子聊下.class檔案的結構。