java類文件結構筆記


注:新的博客地址 - https://zhengw-tech.com/archives/

 

我們都知道java實現跨平台靠的是虛擬機技術,將源文件編譯成與操作系統無關的,只有虛擬機能識別並執行的字節碼文件,由各個操作系統上的jvm來負責執行,屏蔽了底層具體的操作系統。這里我們就來認識一下這個只有jvm才認識的字節碼文件的真實樣子。

為了節省空間,類文件中沒有任何分隔符,各個數據項都是一個挨着一個緊湊排列的,所以其中無論是順序還是數量等都是嚴格規定的,哪個字節代表什么含義,長度是多少,先后順序如何,都不允許改變。下面我們先看一下類文件的整體結構:

Class文件結構

 

 

 其中常量、接口、字段、方法和屬性在其中按各自的結構緊密排列,個數由其前面的數量字段決定。同時類文件中最小單位為1個字節,超過一個字節的數據以大端方式存儲。

 下面依次介紹其中的每個部分:

魔數

魔數是用來確定文件的類型是否是class文件,因為只靠文件擴展名來確定文件類型並不可靠。

這個魔數占文件的開始4個字節,為CA FE BA BE。(注意:這里的字面代表的是十六進制數,而不是ASCII碼)

版本號

接下來的4個字節為class文件版本號,其中前兩個字節表示的是次版本號,后兩個字節表示的是主版本號(從45開始)。

虛擬機可以向下兼容運行class文件,但不能運行高於其版本的class文件。

常量池

由於常量池中的常量數量是不確定的,所以在常量池的入口需要有兩個字節用來代表常量池容量計數值(常量池索引從1開始)。

一共有14種常量類型,有着各自對應的結構,但開始的一個字節同樣都是表示標志位,用來區分不同的類型。

下面為14種常量的具體類型和對應的標志位:

每種類型的結構如下(其中u1表示1個字節,u2表示2個字節,其他同理):

 

 

讀取常量池的時候首先讀取標志位,判斷常量類型,就可以知道對應的結構,獲取對應的信息了。

訪問標志

 常量池之后的兩個字節代表訪問標志,即這個class是類還是接口,是否為public等的信息。不同的含義有不同的標志值(沒有用到的標志位一律為0。),具體信息如下:

 

 類索引

類索引占兩個字節,分別指向常量池中的CONSTANT_Class_info類型的常量,這個類型的常量結構見常量池中的圖表,其中包含一個指向全限定名常量項的索引。

父類索引

因為java只允許單繼承,所以只有一個父類,具體內容同上-類索引。

 接口索引

接口索引開始兩個字節用來表示接口的數量,之后的每兩個字節表示一個接口索引,用法同類索引與父類索引。

字段

字段用於描述接口或者類中聲明的變量,包括類級變量以及實例變量,但不包括局部變量。

字段域的開始兩個字節表示字段數量,之后為緊密排列的字段結構體數據,其結構如下:

其中的字段和方法的描述符,對於字段來說用來描述字段的數據類型;而對於方法來說,描述的就是方法的參數列表(包括數量、類型以及順序)和返回值,這個描述順序也是固定的,必須是參數列表在前,返回值在后,參數列表必須放在一組小括號內。同時為了節省空間,各種數據類型都使用規定的一個字母來表示,具體如下:

對象使用L加上對象的全限定名來表示,而數組則是在每一個維度前添加一個"["來描述。

屬性表在之后進行介紹。

方法

class文件中對方法的描述與以前對字段的描述幾乎采用了完全一致的方式,唯一的區別就是訪問類型不完全一致。

屬性

java7中預定義了21項屬性,具體內容限於篇幅不再列出。

對於每個屬性的結構,沒有特別嚴格的要求,並且可以自定義屬性信息,jvm運行時會忽略不認識的屬性。

符合規范的屬性表基本結構如下:

其中前兩個字節為指向常量池中的CONSTANT_Utf8_info類型的屬性名稱,之后4個字節表示屬性值所占用的位數,最后就是具體屬性了。

 其中有一個比較重要的名稱為「Code」的屬性為方法的代碼,即字節碼指令。

Code屬性表結構如下:

 

 

 

以上只列出了一些Class文件最基本的結構,如有錯誤歡迎指正。

另:目前准備寫一個基於字節碼,分析方法(類、包)間的調用關系工具,項目地址:https://github.com/zavier/jclass-relation ,歡迎有興趣的同學PR

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM