天天看點

JVM 位元組碼_1淺嘗助記符Class檔案結構中常量池中11種資料類型的結構總表

淺嘗助記符

本章節僅僅是介紹有這些個東西,具體内容詳解見後續

package com.ssy.jvm.bytecode;

public class MyTest1 {
    private int a = 1;

    public MyTest1() {
    }

    public int getA() {
        return a;
    }

    public void setA(int a) {
        this.a = a;
    }

}

                

    在Terminal中看反編譯内容,執行語句

javap -c com.ssy.jvm.bytecode.MyTest1

結果

Compiled from "MyTest1.java"
public class com.ssy.jvm.bytecode.MyTest1 {
  public com.ssy.jvm.bytecode.MyTest1();
    Code:
       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

  public int getA();
    Code:
       0: aload_0
       1: getfield      #2                  // Field a:I
       4: ireturn

  public void setA(int);
    Code:
       0: aload_0
       1: iload_1
       2: putfield      #2                  // Field a:I
       5: return
}
                

    若想看更加詳細的一些資訊:

javap -verbose com.ssy.jvm.bytecode.MyTest1

  • 使用javap -verbose指令分析一個位元組碼檔案時,将會分析該位元組碼檔案的魔數、版本号、常量池、類資訊、類的構造方法、類中的方法資訊、類變量與成員變量等資訊。
  • 魔數:所有的.class位元組碼檔案的前4個位元組都是魔數,魔數值為固定值:0xCAFEBABE.(咖啡寶貝哈哈哈哈)

結果

Classfile /Users/ddcc/IdeaProjects/jvm_lecture/out/production/classes/com/ssy/jvm/bytecode/MyTest1.class
  Last modified 2018-10-6; size 491 bytes
  MD5 checksum 4b153cec89fc777dd81db84e9d0b7178
  Compiled from "MyTest1.java"
public class com.ssy.jvm.bytecode.MyTest1
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #4.#20         // java/lang/Object."<init>":()V
   #2 = Fieldref           #3.#21         // com/ssy/jvm/bytecode/MyTest1.a:I
   #3 = Class              #22            // com/ssy/jvm/bytecode/MyTest1
   #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/ssy/jvm/bytecode/MyTest1;
  #14 = Utf8               getA
  #15 = Utf8               ()I
  #16 = Utf8               setA
  #17 = Utf8               (I)V
  #18 = Utf8               SourceFile
  #19 = Utf8               MyTest1.java
  #20 = NameAndType        #7:#8          // "<init>":()V
  #21 = NameAndType        #5:#6          // a:I
  #22 = Utf8               com/ssy/jvm/bytecode/MyTest1
  #23 = Utf8               java/lang/Object
{
  public com.ssy.jvm.bytecode.MyTest1();
    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 6: 0
        line 4: 4
        line 7: 9
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      10     0  this   Lcom/ssy/jvm/bytecode/MyTest1;

  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 10: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/ssy/jvm/bytecode/MyTest1;

  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 14: 0
        line 15: 5
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       6     0  this   Lcom/ssy/jvm/bytecode/MyTest1;
            0       6     1     a   I
}
SourceFile: "MyTest1.java"
                

真正的二進制檔案

Mac推薦使用Hex Fiend工具。

找到MyTest1.class檔案打開:

JVM 位元組碼_1淺嘗助記符Class檔案結構中常量池中11種資料類型的結構總表

我們來針對上面的位元組碼檔案來分析一下含義。

  1. 前4個位元組是固定的0xCAFEBABE。
  2. 緊接着的4個位元組是版本号資訊。前兩個是次版本号,後面兩個是主版本号。我們對照着javap -verbose
    JVM 位元組碼_1淺嘗助記符Class檔案結構中常量池中11種資料類型的結構總表
    是以,前兩個位元組是0,代表次版本号;後兩個位元組是34(3*16+4=52),也就是主版本号。52對應jdk8等。是以該檔案的版本号為1.8.0.可以通過java -version指令來驗證這一點。
  3. 常量池(constant pool):緊接着主版本号之後的就是常量池入口。一個Java類定義的很多資訊都是由常量池來維護和描述的,可以将常量池看作是class檔案的資源倉庫,比如說Java類中定義的方法和變量資訊,都是存儲在常量池中。常量池中主要存儲兩類常量:字面量與符号引用。字面量如文本字元串,Java中聲明為final的常量值等。而符号引用如類和接口的全局限定名,字段的名稱和描述符,方法的名稱和描述符等。
  4. 常量池的總體結構:Java類所對應的常量池主要由常量池數量與常量池數組(又叫做常量表)這兩部分共同構成。常量池數量緊跟在主版本号後面,占據2個位元組;常量池數組則緊跟在常量池數量之後。常量池數組與一般的數組不同的是,常量池數組中不同的元素的類型、結構都不是不同的,長度當然也就不同;但是每一種元素的第一個資料都是一個u1類型,該位元組是個辨別位,占據了一個位元組。JVM在解析常量池時,會根據這個u1類型來擷取元素的具體類型。值得注意的是,常量池數組中元素個數 = 常量池數量 - 1 (其中0暫時不使用),目的是滿足某些常量池索引值的資料在特定情況下需要表達【不引用任何一個常量池】的含義;根本原因在于,索引為0也是一個常量(保留常量),隻不過它不位于常量表中,這個常量就對應null值;是以常量池的索引從1而非0開始。
  5. 在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;

  6. 對于數組類型來說,每一個次元用一個前置的[來表示,如int[]表示為

    [I

    ; String[][]被表示為

    [[Ljava/lang/String

    ;
  7. 用描述符來描述方法的時候,按照先參數清單,後傳回值的順序來描述,參數清單按照參數的嚴格順序放在一組()之内,如方法String getRealNameByIdAndNickName(int id, String name)的描述符為:

    (I,Ljava/lang/String;)Ljava/lang/String;

Class檔案結構中常量池中11種資料類型的結構總表

現在不止11個,新增了一些getStaticRef什麼什麼的。。

Class檔案結構中常量池中11種資料類型的結構總表
常量 項目 類型 描述
CONSTANT_Utf8_info tag U1 值為1
length U2 UTF-8編碼的字元串長度
bytes U1 長度為length的UTF-8編碼的字元串
CONSTANT_Integer_info tag U1 值為3
bytes U4 按照高位在前存儲的int值
CONSTANT_Float_info tag U1 值為4
bytes U4 按照高位在前存儲的float值
CONSTANT_Long_info tag U1 值為5
bytes U8 按照高位在前存儲的long值
CONSTANT_Double_info tag U1 值為6
bytes U8 按照高位在前存儲的double值
CONSTANT_Class_info tag U1 值為7
index U2 指向全限定名常量項的索引
CONSTANT_String_info tag U1 值為8
index U2 指向字元串字面量的索引
CONSTANT_Fieldref_info tag U1 值為9
index U2 指向聲明字段的類或者接口描述符Constant_Class_info的索引項
index U2 指向字段描述符CONSTANT_NameAndType_info索引項
CONSTANT_Methodref_info tag U1 值為10
index U2 指向聲明方法的類描述符CONSTANT_Class_info的索引項
index U2 指向名稱及類型描述符CONSTANT_NameAndType_info的索引項
CONSTANT_InterfaceMethodref_info tag U1 值為11
index U2 指向聲明方法的接口描述符CONSTANT_Class_info的索引項
index U2 指向名稱及類型描述符CONSTANT_NameAndType_info的索引項
CONSTANT_NameAndType_info tag U1 值為12
index U2 指向字段或方法名稱常量項的索引
index U2 指向字段或方法描述符常量項的索引