1. 程式人生 > >Class檔案結構(七)

Class檔案結構(七)

Class檔案是平臺無關性的基礎之一

平臺無關性和語言無關性。Java的一個非常著名的宣傳口號:“一次編寫,到處執行(Write Once,Run Anywhere)”。Sun公司以及其他虛擬機器提供商釋出了許多可以執行在各種不同平臺上的虛擬機器,這些虛擬機器都可以載入和執行同一種平臺無關的位元組碼,從而實現了程式的“一次編寫,到處執行”。各種不同平臺的虛擬機器與所有平臺都統一使用的程式儲存格式——位元組碼(ByteCode)是構成平臺無關性的基石。 同時語言無關性也越來越被開發者所重視。到目前為止大部分程式設計師還認為Java虛擬機器執行Java程式是一件理所應當和天經地義的事情。時至今日,商業機構和開源機構已經在Java語言之外發展出一大批在Java虛擬機器之上執行的語言

,如Clojure,Groovy,JRuby,Jython,Scala等。實現語言無關性的基礎仍然是虛擬機器和位元組碼儲存格式。Java虛擬機器不和包括Java在內的任何語言繫結,他只與“Class檔案”這種特定的二進位制檔案格式所關聯,在滿足Java虛擬機器規範的要求下,任何一門功能性語言都可以表示為一個能被Java虛擬機器所接受的有效的Class檔案。例:除了Java語言,JRuby等同樣可以通過編譯器得到Class檔案,虛擬機器不關心Class檔案的來源是何種語言

Class類檔案結構:

Class檔案是一組以8位位元組為基礎單元的二進位制資料流。各個資料專案嚴格按照順序緊湊的排列在Class檔案之中。中間沒有新增任何分隔符

,這使得整個Class檔案儲存的內容幾乎全部都是程式執行的必要資料。Class檔案採用一種類似於C語言結構體的偽結構來儲存資料,這種偽結構中只有兩種資料型別:無符號數和表無符號數屬於基本的資料型別,以u1,u2,u4,u8來分別代表1個位元組,2個位元組,4個位元組,8個位元組的無符號數,無符號數可以用來描述數字、索引引用、數量值或者按照Utf-8編碼構成字串值。表是由多個無符號數或者其他表作為資料項構成的複合資料型別,所有表都習慣性的以“_info”結尾。如圖:表中資料項無論是順序還是數量,甚至於資料儲存的位元組序都是被嚴格限定的,不允許改變

示例程式碼:

public class TestClass {

    private int m;

    public int inc(){

        return m + 1;

    }

}

以下Class檔案的位元組碼檔案出自該程式碼片段。

Class結構資料項分析:(魔數、檔案的版本號、次版本號、主版本號、常量池、訪問標誌

魔數:每個Class檔案的頭4個位元組稱為魔數(Magic Number),他的唯一作用是確定這個檔案是否為能被虛擬機器接受的Class檔案。使用魔數而不是副檔名來進行識別主要基於安全方面的考慮(gif、jpeg等也使用),因為副檔名可以隨意改動。值為:0xCAFEBABE(咖啡寶貝),緊接著魔數的4個位元組儲存的是Class檔案的版本號,第5和第6個位元組是次版本號(Minor Version),第7和第8個位元組是主版本號(Major Version)。Java的版本號是從45開始的,JDK1.1之後的每個JDK大版本釋出主版本號向上加1(JDK1.0~1.1使用了45.0~45.1的版本號),高版本的JDK能向下相容以前的版本的Class檔案,但不能執行以後版本的Class檔案,即使檔案格式未發生任何變化,虛擬機器也必須拒絕超過其版本號的Class檔案。如圖(winhex開啟):地址為:0x00000007(33)。十六進位制33轉成十進位制51代表JDK1.7。

常量池:緊接主版本號之後的是常量池的入口。常量池可以理解為Class檔案之中的資源倉庫。它是Class檔案結構中與其他組成部分關聯最多的資料型別,也是佔用Class檔案空間最大的資料部分之一,同時他還是在Class檔案中第一個出現表型別資料的部分。由於常量池中常量數量不是固定的,所以在常量池的入口需要放置一項u2型別的資料代表常量池容量計數值(constant_pool_count)。常量池中主要存放兩大類常量:字面量(Literal)和符號引用(Symbolic References)。字面量比較接近於Java語言層面的常量概念,如文字字串、宣告final的常量值等。而符號引用則包括下面三類常量:

  1. 類和介面的全限定名(Fully Qualified Name)
  2. 欄位的名稱和描述符(Descriptor)
  3. 方法的名稱和描述符

由於Java的“連線”步驟發生在類載入的時候,所以在Class檔案中不會儲存各個方法欄位的最終記憶體佈局資訊,因此這些欄位、方法的符號引用不經過執行期轉換的話無法得到真正的記憶體入口地址,也就無法直接被虛擬機器使用。當虛擬機器執行時需要從常量池獲得對應的符號引用,再在類建立時或執行時解析、翻譯到具體的記憶體地址之中常量池中的每一項常量都是一個表(資料結構),這些表都有一個共同特點,即表開始的第一位是一個u1型別的標誌位,代表當前常量屬於哪種常量型別。其中每一種表(常量型別)都有自己的結構。如圖: