
1.2 Java 位元組碼結構
Java 源檔案通過用 javac 指令編譯後就會得到 .class 結尾的位元組碼檔案,比如一個簡單的 JavaCodeCompilerDemo 類如圖 2 所示:
編譯後生成的 .class 位元組碼檔案,打開後是一堆十六進制數,如圖 3 所示:
圖 4
0xCAFEBABE
,魔數放在檔案頭,JVM 可以根據檔案的開頭來判斷這個檔案是否可能是一個位元組碼檔案,如果是,才會進行之後的操作。
(3)常量池(Constant Pool) 緊接着主版本号之後的位元組是常量池入口。常量池中存儲兩種類型常量: 字面量和符号運用。字面量為代碼中聲明為 圖 5
常量池計數器(constant_pool_count): 由于常量池的數量不固定,是以需要先放置兩個位元組來表示常量池容量計數值,圖 2 示例代碼的位元組碼的前十個位元組如下圖 6 所示,将十六進制的 17 轉為十進制的值為 33 (1 * 16^1 + 7 * 16^0 = 33),排除下标 0,也就是說這個類檔案有 32 個常量。
常量池資料區: 資料區是由(constant_pool_count - 1)個 cp_info 結構組成,一個 cp_info 的結構對應一個常量。在位元組碼中共有 14 種類型的 cp_info ,每種類型的結構都是固定的,如圖 7 所示
圖 7
表 1
javap -verbose JavaCodeCompilerDemo
指令檢視 JVM 反編譯後的完整常量池,可以看到反編譯結果可以将每一個 cp_info 結構的類型和值都很明确的呈現出來,如圖 9 所示
(4)通路标志(access_flag) 常量池結束之後的兩個位元組,描述該 Class 是類還是接口,以及是否被 表 2
(7)接口資訊(interfaces) 父類名稱後的兩個位元組,描述這個類的接口計數器,即: 目前類或父類實作的接口數量。緊接着的 n 個位元組是所有的接口名稱的字元串常量在常量池的索引值。
以圖 3 中的位元組碼字段表為例,如下圖 11 所示。其中字段的通路标志查表 2,002 對應為 Private,通過索引下标在圖 9 中常量池分别得到字段名為: numberA,描述符為: I(在JVM 中的I代表 Java 中的 int)。綜上,就可以唯一确定出類 JavaCodeCompilerDemo 中聲明的變量為: private int numberA 。
1.LocalVariableTable: 本地變量表,包含 this 和局部變量,之是以可以在每一個非 static 的方法内部都可以調用到 this,是因為 JVM 将 this 作為每個方法的第一個參數隐式進行傳入。
(10)附加屬性表(additional_attribute_table) 位元組碼的最後一部分,存放了在檔案中類或接口所定義的屬性的基本資訊。