深刻解析Class類文件的結構

前言

要深刻學習Java以及Java虛擬機,深刻學習Java字節碼文件是繞不開的一條路,只有知道了字節碼文件裏的排列結構,你才能透徹的瞭解在JVM裏,類加載是怎麼加載Java類的,是怎麼將二進制流轉化爲運行時數據結構的。html

Class文件是是一組以8字節爲基礎單位的二進制流,各個數據項目嚴格按照順序緊湊地排列在Class文件中,中間沒有任何分隔符。java

這裏的Class文件其實不是特指Java的字節碼文件,任何編程語言的編譯器只要按照字節碼文件規範編譯成Class文件,均可以在JVM上運行,因此字節碼文件和JVM是和語言無關的。c++

另一般Class文件指的不必定是存儲在磁盤上的以.class後綴結束的文件,是一種泛指,指的是一切按照字節碼文件規範排列的二進制字節流。編程

字節碼文件解析

Class文件採用下面這種相似C語言的結構體的僞結構來存儲數據,整個Class文件是一張表,表裏又由無符號數和表組成。數組

ClassFile { 
    u4  magic; // 魔數,固定爲"0xCAFEBABY"
    u2  minor_version; //jdk次版本號
    u2  major_version;  //jdk主版本號
    u2  constant_pool_count;  //常量池數組大小,從1計數
    cp_info  constant_pool[constant_pool_count - 1]; //常量池數組
    u2  access_flags;  //類的訪問標誌,如:public
    u2  this_class;  //類索引,指向常量池中的類符號引用
    u2  super_class;  //父類索引,指向常量池中的類符號引用
    u2  interfaces_count; //實現的接口的數量
    u2  interfaces[interfaces_count]; //接口列表,按implements後面的接口順序
    u2  fields_count;  //字段數
    field_info  fields[fields_count]; //字段表
    u2  methods_count; //方法數
    method_info  methods[methods_count]; //方法表
    u2  attributes_count; //屬性表大小
    attribute_info  attributes[attributes_count]; //屬性表
}

複製代碼

從上面的僞結構能夠看到,Class文件根據上面的順序把規定的數據類型按照佔用的字節依次排列下來。bash

下面經過一個例子來實戰分析一下Class文件數據結構

//Test.class
public class Test { 
    public static int a = 1;
    public static final int b = 1; 
    public void say(){
        System.out.println("Hello");
    }
}
複製代碼

字節碼文件實戰分析
上圖是編譯後的Test.class文件的二進制數據,能夠按照上面ClassFile的結構順序依次分析下,下面是部分分析結果:
(1) u4 magic
    4個字節(000h:0123)魔數: 0xCAFEBABY

(2) u2 minor_version
    2個字節(000h:45)次版本號: 0x0000, 次版本號爲0編程語言

(3) u2 major_version
    2個字節(000h:67)主版本號: 0x0034,即52,JDK1.0-1.1:45.0 ~ 45.3, 1.1後版本增1,數字加1,因此這裏用的是1.1 + 0.(52-45) = 1.8工具

(4) u2 constant_pool_count
    2個字節(000h:89)常量池大小:0x0027,即39,常量池數組是從1開始計數的,說明常量池中有38個常量,後面依次排列的就是常量池的38個常量post

(5) cp_info constant_pool[constant_pool_count - 1]
    常量池所佔的字節數是由常量池中常量的數量以及類型所決定的,這裏有38個常量,每一個常量開頭都有一個字節的tag標識常量的類型,具體類型能夠參考最下面的腦圖,根據這個標識能夠找到這個常量所佔的字節以及含義,下面分析其中一個常量,其他的讀者有興趣能夠所有完成

  • 000h:a 0x0A,表示常量類型爲10,查表可知是CONSTANT_Methodref方法符號引用,那接下來的四個字節,前兩個字節表示指向常量池中方法所在類的符號引用的索引項,就是常量池的數組下標,所在的位置是方法所在類的符號引用
  • 000h:bc 0x0007,指向常量池數組第7個元素,第7個常量是一個java.lang.Object類的符號引用
  • 000h:de 0x0018, 指向常量池數組的第24個元素,第24個常量是一個名稱和類型的符號引用,方法名是<init>,描述符是()V

這樣第一個常量就分析完成,共佔5個字節,表示的是方法符號引用,該方法所在的類是Object類,方法名稱是<init>, 無參數,返回值是void

藉助工具javap能夠更直觀的看到咱們剛剛分析的部分結果以及所有類文件的結構,使用如下命令便可:

javap -v Test.class
複製代碼

結果如圖:

javap.png

經過上面的圖能夠看到,和咱們上面的部分分析是一致的

Class文件結構腦圖

下面是我在看《深刻理解Java虛擬機》這本書的時候整理的關於Class文件結構的腦圖,圖片比較大,右鍵另存爲圖片再查看會更方便。

class.png

原文連接:www.jackielee.cn/posts/7eb7d…
更多幹貨請掃碼關注

程序猿隨記
相關文章
相關標籤/搜索