Class文件解讀(二)

接上篇:http://www.javashuo.com/article/p-dqbwnfmb-ma.htmljava

常量池(constant_pool_count、constant_pool)工具

        Class文件中緊接着主次版本號以後的是常量池入口,常量池能夠理解爲Class文件中的資源倉庫,由於Class文件中的絕大部分的字面量及符號引用就存儲與常量池中。它是Class文件結構中與其餘項目關聯最多的數據類型,也是佔用Class文件空間最大的數據項目之一,同時它仍是在Class文件中第一個出現的表類型數據項目。佈局

    因爲常量池(constant_pool)中常量的數量是不固定的,所以使用了一個常量池容量計數值(constant_pool_count)來表示某一Class文件中具體常量池中的常量數目,這裏須要知道常量池的容量計數是從1開始而不是0,而其餘集合類型,如:接口索引集合、字段表集合、方法表集合等容量計數都是從0開始。從圖一中能夠看出該Class文件中常量池容量值偏移量爲00000008,值爲Ox19,轉爲十進制也就是25,則可算得該Class文件中的常量池容量爲24(25-1)。編碼

                        

                                                                   圖1   常量池結構.net

     上文中有闡述到常量池中主要存放的是兩大類常量:字面量(Literal)及符號引用(Symbolic References),字面量比較接近於java語言中常量的概念,如文本字符串(類名、方法名等)、聲明爲final的常量值等,而符號引用則屬於編譯原理方面的概念,包括下面三類常量:翻譯

  • 類和接口的全限定名(Fully Qualified Name)
  • 字段的名稱和描述符(Descriptor)
  • 方法的名稱和描述符

        java代碼在進行javac編譯的時候,並不像C和C++那樣有「鏈接」這一步驟,而是在虛擬機加載Class文件的時候進行動態鏈接。也就是說,在Class文件中不會保持各個方法、字段的最終內存佈局信息,所以這些字段、方法的符號引用不通過運行期轉換的話沒法獲得真正的內存入口地址,也就沒法直接被虛擬機使用。當虛擬機運行時,須要從常量池中獲取對應的符號引用,再在類建立時或運行時解析、翻譯到具體的內存地址之中。blog

        常量池中每一項常量都是一個表,在JDK1.7以前共有11種結構各不相同的表結構數據,在JDK1.7中爲了更好地支持動態語言調用(動態語言區別於靜態語言的一個重要特徵是:動態語言的類型檢查的主體過程在運行期而不是在編譯期),增長了(CONSTANT_MethodHandle_info、CONSTANT_MethodType_info和CONSTANT_InvokeDynamic_info)。索引

        這14種表都有一個共同的特色,就是表開始的第一位是一個u1類型的標誌位(tag,取值見下表中標誌列),表明當前這個常量屬於哪一種常量類型。接口

                                                                        表1  常量池的項目類型ip

                   

        之因此說常量池是最繁瑣的數據,是由於這14種常量類型各自均有本身的結構。回頭看圖1中常量池的第一項常量,它的標誌位(偏移地址:Ox0000000A)是Ox0A,查表1的標誌列發現這個常量屬於CONSTANT_Methodref_info類型,此類型的常量表示類中方法的符號引用。CONSTANT_Methodref_info的結構見表2。

                                                表2  CONSTANT_Methodref_info型常量的結構

                                       

      tag是標誌位,用於區分常量類型;class_index是一個索引值,它指向聲明方法的類的描述符CONSTANT_class_info的索引項,這裏class_index的值(偏移地址:Ox0000000B)爲Ox0004,也便是指向了常量池中的第四項常量,結合表1常量池的項目類型在圖一中找到第四位常量,偏移地址爲Ox00000010,值爲Ox07,獲得第四位常量是類型爲CONSTANT_class_info類型的常量,該類型常量的結構見表3。

                                                    表3  CONSTANT_class_info型常量的結構

                                         

此常量表明瞭這個類(或接口)的全限定名,這裏name_index值(偏移地址Ox00000018)爲Ox0018,也便是指向了常量池中的第二十四項常量,這裏就不一個一個去數啦,後面有工具能夠直接用,最終會發現第二十四項常量的類型爲CONSTANT_utf8_info類型(在常量池中該常量類型基本是最多的)。該類型常量結構見表4。

                                                   表4  CONSTANT_utf8_info型常量的結構

                                                  

tag值爲7(見表1),length表示這個使用UTF-8編碼的字符串佔用的字節數,它後面緊跟着的長度爲length字節的連續數據是一個使用UTF-8縮略編碼的字符串,這裏使用縮略編碼而不是普通的UTF-8編碼,主要是爲了節省空間,體現出class文件的「精湛」。值得一提的是,因爲Class文件中方法、字段等都須要引用CONSTANT_Utf8_info型常量來描述名稱,因此length的最大長度也就是java中方法、字段名的最大長度。而這裏的最大長度就是u2能表示的最大值65535,因此當java程序中定義了超過64KB英文字符的變量或方法名,將會沒法編譯。對於在本例中第二十四位常量裏面表示的常量值爲方便起見咱們藉助javap工具進行查看(固然也能夠經過class文件中的對應字節算出來),以下圖:

                                                

                                                            圖2  常量池中具體常量

上圖詳細列出了示例代碼編譯後的Class文件中,常量池裏面的具體常量類型、常量值及相應的符號引用,從中咱們能夠看出該Class文件中的第二十四位常量值爲java/lang/Object。咱們再回過頭來看常量池中第一位常量類型爲CONSTANT_Methodref_info的常量,該常量裏的NameAndType_index可從圖2中看到它分別指向了第十位和第十一位常量,第十位常量<init>是方法名,而第十一位常量是該方法的方法描述符,具體的方法描述符的描述規則會在下文中闡述。

       至此咱們完整地將本例常量池中的第一位常量類型爲CONSTANT_Methodref_info的常量含義,一步一步地闡述清楚,對於常量池中的其餘常量亦可用上述的方法進行閱讀理解。爲方便讀者進一步瞭解常量池,下面給出14種常量類型的結構:

           

          行文至此,Class文件結構中的常量池相關內容已介紹完畢,但願讀者能有所獲。

下篇:http://www.javashuo.com/article/p-oibmohyh-mb.html

相關文章
相關標籤/搜索