1. 程式人生 > >JVM中class文件探索與解析(一)

JVM中class文件探索與解析(一)

範圍 protected test except itl 指向 strac 相關 父類索引

一直想成為一名優秀的架構師的我,轉眼已經工作快兩年了,對於java內核了解甚少,閑來時間,看看JVM,吧自己的一些研究寫下來供大家參考,有不對的地方請指正。

廢話不多說,一起來看看JVM中類文件是如何加載和運行的。

(1)首先,編寫簡單代碼,對其編譯生成的class文件進行研究,其java代碼如下:

技術分享
 1 public class test {
 2     private static int count = 0;
 3     public static void recursion(){
 4         count++;
 5         recursion();
 6     }
7 8 public static void main (String args[]){ 9 try { 10 recursion(); 11 }catch (Exception ex){ 12 System.out.print("deep of callings:"+count+"\n"); 13 ex.printStackTrace(); 14 } 15 } 16 }
View Code

編譯之後,用WinHex軟件打開其class文件,可以看到其編譯的十六進制文件如下:

技術分享

  按照上圖分析,開頭的前4個字節技術分享,是魔數(類似於拼音“咖啡寶貝”),它的用處是標識該文件是否能被java虛擬機識別;

  緊接著魔數的4個字節技術分享,前兩字節0x00代表次版本號(小數點之後的數字),後兩字節0x0033代表是class文件的主版本號,換算成十進制是51,標識是JDK1.7可識別的版本(不同的版本可以查看class文件版本號表如下:)

版本號對應十進制jdk版本號
2E 46 jdk1.2
2F 47 jdk1.3
30 48 jdk1.4
31 49 jdk1.5
32 50 jdk1.6
33 51 jdk1.7
34 52 jdk1.8

  在主版本號字節之後的是常量池,可以理解為Class文件的資源倉庫,存儲著與class文件相關的數據項。由於不同class文件,常量池數量不同,常量池入口放置兩個字節的數據技術分享(0x0028)為常量池計數器。十六進制的0x0028為十進制的40(地址偏移量),代表常量池中有39個常量,索引範圍為1-40(註:java僅限於class文件結構中容量計數器是從1開始的,java的設計者將索引0拿出來是有特殊考慮的,用來表示不引用任何一個常量池中的項)。

  常量池中,存放兩類數據:(1)字面量:可以理解為java中的常量,例如:字符串、final修飾常量等。

              (2)符號引用:主要包括①類、接口的全限定名②字段的名稱和描述符③方法的名稱和描述符

  在常量池裏,存儲常量結構如下:u1(常量標誌位,用於指明常量的類型,可以查看如下常量池項目類型對應表)+常量信息

技術分享

  讓我們以上述class文件為例,索引為1的常量標誌位是0x0A(十進制為10),對應上表中的CONSTANT_Methoddef_info類型的常量,參考常量結構表如下圖(在jdk1.7中新增了tag=15/16/18的常量類型,更好的支持動態語言的調用,此處就不列舉了),

技術分享

  該class文件中,常量池裏索引為1的常量(const#1),項目類型標識符為0x0A,二進制為10,查詢上表,代表著類方法的符號引用。緊接著兩個u2字符代表該常量的信息內容,其中方法描述符0x0006為#6常量,名稱及類型描述為0x001A指向#26常量。

  緊跟其後的是索引為2的常量(const#2),其標誌符為0x09(十進制為9),是字段的符號引用,緊接著的兩個u2字符代表其引用索引ID,方法的類描述符指向#27常量,字段描述符指向#28常量;

  分析了以上兩個字節之後,這裏就不一一分析後面的常量了,有興趣的可以自己分析下。其他的常量池用jdk自帶的javap進行生成,在windows中打開cmd(安裝jdk並配置環境變量),輸入:javap -verbose class文件路徑,可以看到編譯之後的常量池如下:

技術分享

  將上述class文件中常量池部分標記圖如下,紅色框代表一個常量池中的項,依次編號為1-39,

技術分享

  我們將上圖和javap生成的常量內容對比一下,以const#9為例,#9項為:0x01 (utf8類型) 0x0006(占用字節) 0x3C 0x69 0x6E 0x69 0x74 0x3E(項內容),我們對項內容進行在線轉換,將十六進制轉換ASCII碼值,得到該常量表示:<init>,如下圖:

技術分享

  與javap生成的常量文件對比,發現兩者完全一致。對字節碼有興趣的朋友可以逐個試一試。

技術分享

  在常量池區域結束之後,緊接著的一個u2(兩個字節)類型的字符代表訪問標誌,它用於識別類或者接口的訪問信息,例如:class是類還是接口,訪問是private還是public等。訪問標誌表如下圖:

技術分享

  在上述文件中,訪問標誌為:技術分享,即:0x0021,對照上表,只有ACC_PUBLIC和ACC_SUPER為真,其他幾項為假。該類為public 能夠使用invoke指令。

  跟在訪問標誌之後的分別是類索引、父類索引技術分享。由於java不允許多繼承,所以類索引和父類索引是一個u2類型的數據。在上述文件中,類索引為#5常量(TestClass),父類索引為#6常量(java/lang/Object);

  緊接著類索引和父類索引的是接口索引信息。在java中一個類可以實現多個接口,所以用u2類型的數據集合來表示接口索引。在接口索引的入口,有一項u2類型的接口計數器技術分享,計數器為0表示接口的索引表不占用任何字節。

  在接口相關描述信息之後的,是字段表集合,用於描述類或者接口中申明的變量(註:此處的變量是指類或者接口級變量,即類變量或者實例級變量,而不包括方法中的局部變量)。這些字段通常包含哪些信息呢?通常有:字段訪問域(private、public、protected等)+是實例變量還是類變量(Static)+是否可修改(final)+並發可見性(volatitle,用volatile修飾的變量,線程在每次使用變量的時候,都會讀取變量修改後的最終的值)+可否序列化(transient)+字段類型(基本類型、對象、數組)+字段名稱。在JVM中,字段表結構如下:

類型     名稱 數量
u2 access_flags 1
u2 name_index 1
u2 descriptor_index 1
u2 attributes_count 1
attribute_info attributes attributes_count

  我們來看,字段表集合的入口u2類型數據項是字段表的容量計數器為技術分享(0x0001),表示只有一個字段項。緊接著字段表容量計數器的u2類型的數據為0x0002,參考下表,表示該字段為private。字段名稱技術分享為0x0007,其值為“m”,描述信息0x0008,其值為“I”,可以推斷,原代碼的定義字段為:“private int m”;

標誌名稱 標誌值 含義
ACC_PUBLIC 0x00 01 字段是否為public
ACC_PRIVATE 0x00 02 字段是否為private
ACC_PROTECTED 0x00 04 字段是否為protected
ACC_STATIC 0x00 08 字段是否為static
ACC_FINAL 0x00 10 字段是否為final
ACC_VOLATILE 0x00 40 字段是否為volatile
ACC_TRANSTENT 0x00 80 字段是否為transient
ACC_SYNCHETIC 0x10 00 字段是否為由編譯器自動產生
ACC_ENUM 0x40 00 字段是否為enum

  通常而言,在字段描述之後還有一些屬性表信息存儲額外的信息,在以上class文件中,屬性計數器位0x0000,表示沒有額外的屬性信息。

  在字段表之後的是方法表集合,其表示方法與字段信息表幾乎一致。其結構如下表:

類型     名稱 數量
u2 access_flags 1
u2 name_index 1
u2 descriptor_index 1
u2 attributes_count 1
attribute_info attributes attributes_count

  但是,方法表的修飾屬性比字段表要多,例如:方法有abstract、synchronize等。在方法表的入口,同樣也有一個方法容量計數器,占用u2字節。在上述class中,方法的計數器為技術分享,即0x0003表示有三個方法。

  第一個方法,function#1的第一、二、三、四、五項u2數據項分別為:0x0001、0x0009、0x000A、0x0001、0x000B,代表方法為public、方法名指向const#9("<init>")、方法描述為const#10(“()V”)、含有一個屬性、該屬性指向const#11(“Code”)屬性,java呈現方法體重的代碼經過javac編譯之後,就存儲在Code屬性裏。

  (ps:今天的class文件探索就寫到這裏,過幾天接著寫...休息一下)

  

  

  

JVM中class文件探索與解析(一)