1. 程式人生 > >深入理解java位元組碼

深入理解java位元組碼

Javap 反編譯class檔案 –verbose 顯示冗餘資訊

(1)魔數:所有的class位元組碼檔案的4個位元組都是魔數,魔數固定值:0xCAFEBABE

(2)版本:魔數之後4個位元組是版本資訊,前兩個位元組minor version次版本號例如0,後兩個位元組是主機板號major version例如52表示1.8.0。

(3)常量池:主版本後就是常量池入口。常量池的長度不是固定的。Java類中定義的很多資訊都是由常量池來維護和描述的。可以將常量池看做是Class檔案的資源倉庫,例如說java類中定義的方法與變數資訊。常量池中主要儲存兩類常量:字面量和符號引用。

         ①字面量:文字字串,final常量。

         ②符號引用:類和介面的全侷限定名。欄位名稱和描述符,方法的名稱和描述符。

         Java類所對應的常量池主要由常量池數量與常量池陣列(也稱常量表)這兩部分共同構成。(這麼做可以最大壓縮儲存檔案體積)。常量池數量緊跟在主版本號後面,佔用2個位元組。常量池陣列僅跟在常量池數量之後。常量池陣列不同與一般陣列,不同元素型別,結構都不相同。每一個元素的第一個陣列都是u1型別,是一個標誌位,佔據1個位元組,JVM在解析常量池時,會根據u1型別來獲取元素的具體型別。值得注意的是,常量池陣列中元素的個數=常量池-1(其中0暫時不使用)。目的是滿足某些常量池索引值的資料在特定情況下所需表達[不引用任何常量池]的含義。索引0是一個保留常量,不位於常量陣列中,對應null值,所以索引從1開始。

(4)在jvm規範中,每個變數/欄位都有描述資訊,描述資訊主要作用是描述欄位的資料型別,方法的引數列表(包括數量、型別、順序)與返回值。根據描述符規則,基本資料型別和代表無返回值的void型別都用一個大寫字元來表示,物件型別則使用字串L加物件的許可權定名來表示。為了壓縮位元組碼的體積,對於基本資料型別,jvm都只使用一個大寫字母來表示,如下所示:B –byte, C –char, D –double, F—float, I—int, J –long S – short, Z – Boolean, V—void, L—物件型別。例如Ljava/lang/String

         對於陣列型別來講,每一個維度使用一個前置的[來表示,如int[]被標記為[I,String[][]被標記為[[Ljava.lang.String 注意:這個是給機器看的

         用描述符描述方法時,按照先引數列表,後返回值的順序來描述。引數列表按照引數的嚴格順序放在一組()之內。例如String getRealnameByIdAndNickname(int id, String name)的描述為:(I,Ljava/lang/string;) Ljava/lang/String;

Constant pool:

   #1 = Methodref          #4.#20         // java/lang/Object."<init>":()V

   #2 = Fieldref           #3.#21         // com/lesson/model/MyTest.a:I

   #3 = Class              #22            // com/lesson/model/MyTest

   #4 = Class              #23            // java/lang/Object

   #5 = Utf8               a

   #6 = Utf8               I

   #7 = Utf8               <init>

   #8 = Utf8               ()V

   #9 = Utf8               Code

  #10 = Utf8               LineNumberTable

  #11 = Utf8               LocalVariableTable

  #12 = Utf8               this

  #13 = Utf8               Lcom/lesson/model/MyTest;

  #14 = Utf8               getA

  #15 = Utf8               ()I

  #16 = Utf8               setA

  #17 = Utf8               (I)V

  #18 = Utf8               SourceFile

  #19 = Utf8               MyTest.java

  #20 = NameAndType        #7:#8          // "<init>":()V

  #21 = NameAndType        #5:#6          // a:I

  #22 = Utf8               com/lesson/model/MyTest

  #23 = Utf8               java/lang/Object

{

  public com.lesson.model.MyTest();

    descriptor: ()V

    flags: ACC_PUBLIC

    Code:

      stack=2, locals=1, args_size=1

         0: aload_0

         1: invokespecial #1                  // Method java/lang/Object."<init>":()V

         4: aload_0

         5: iconst_1

         6: putfield      #2                  // Field a:I

         9: return

      LineNumberTable:

        line 3: 0

        line 5: 4

      LocalVariableTable:

        Start  Length  Slot  Name   Signature

            0      10     0  this   Lcom/lesson/model/MyTest;

 

  public int getA();

    descriptor: ()I

    flags: ACC_PUBLIC

    Code:

      stack=1, locals=1, args_size=1

         0: aload_0

         1: getfield      #2                  // Field a:I

         4: ireturn

      LineNumberTable:

        line 8: 0

      LocalVariableTable:

        Start  Length  Slot  Name   Signature

            0       5     0  this   Lcom/lesson/model/MyTest;

 

  public void setA(int);

    descriptor: (I)V

    flags: ACC_PUBLIC

    Code:

      stack=2, locals=2, args_size=2

         0: aload_0

         1: iload_1

         2: putfield      #2                  // Field a:I

         5: return

      LineNumberTable:

        line 12: 0

        line 13: 5

      LocalVariableTable:

        Start  Length  Slot  Name   Signature

            0       6     0  this   Lcom/lesson/model/MyTest;

            0       6     1     a   I

}

(1) 00 36 表示次版本號例如0,主版本號1.8.0

(2) 00 18 (索引0,陣列長度)常量池索引0位置為保留, 16進位制18即24,也就是陣列長度,但是0是保留常量,所以準確來說23.

(3)0A tag值10 對應constant_methodref_info,佔據4個位元組

前兩個表示指向宣告欄位的類或者介面描述符constant_class_info的索引(00 04--即4)

後兩個表示名稱及型別描述符constant_nameandtype_info的索引項(00 14即20)

#1 = Methodref          #4.#20         // java/lang/Object."<init>":()V

(4)09 tag值9 對應constant_fieldref_info,佔據4個位元組

         前兩個表示欄位的型別或者介面描述符constant_class_info的索引項(00 03即3)

後兩個表示欄位描述符constant_nameandtype_info的索引項(00 15即21)

#2 = Fieldref           #3.#21         // com/lesson/model/MyTest.a:I

         #3-class表示當前類

         #21-對應兩個部分:#5#6; #5--a(欄位名稱)  #6--I(integer)

(5)07 tag值7 對應constant_class_info, 佔據2個位元組

         00 16 即22 com/lesson/model/MyTest類全限定名稱

#3 = Class              #22            // com/lesson/model/MyTest

(6) 07 tag值7對應constant_class_info, 佔據2個位元組

00 17即23 com/lang/object類全限定名稱

(7)01 tag值1 對應constant_utf-8_info, 佔據3個位元組

前兩個表示utf-8編碼字元長度。00 01 即1個位元組

00 01 後面一個位元組是61。61就是a(asc 碼)

(8) 01 tag值1

………………..就這樣一點一點分析就明白了位元組碼

縱觀java位元組碼的整體結構由下面10個部分構成,精確的描述了位元組碼資訊

try-finally分析:

public  static String test(){
    String res  = "he";
    try {
        return res;
    } finally {
        res = "res";
    }
}
0:     ldc           #5     // String he  he壓入棧頂
2:     astore_0             //he 存到本地變數0中
3:     aload_0              //本地變數1中壓棧
4:     astore_1             //he 存到本地變數1中
5:     ldc           #6     // String res res壓入棧頂
7:    astore_0             //res 存到本地變數0中
8:     aload_1              //本地變數1中壓棧
9:     areturn              //返回本地變數1  he
10: astore_2
11: ldc           #6     // String res
13: astore_0
14: aload_2
15: athrow