JVM探索之路之Class文件結構解析(二):常量池


 

JVM 學習筆記目錄:

JVM探索之路之Class文件結構解析(一) :Class文件的格式與定義 

JVM探索之路之Class文件結構解析(三):訪問修飾符、類索引、父類索引與接口索引集合 

 

常量池

  上一篇博文介紹了Class文件的“魔數”和“主次版本號”,常量池數據項目的入口是緊接着“主次版本號”數據項目的。Class文件的常量池是Class文件結構中與其他項目關聯最多的數據類型,也是占用Class文件空間最大的數據項目之一,同時也是Class文件中第一個表類型的數據項目。為了方面講解和查看下面給出Class文件結構表和實例Class文件:

示例Class:

package com.beliefbetrayal.clazz;

public class ClassFileTest {

private int m;

public int getM() {
return m;
}

public void setM(int m) {
this.m = m;
}
}

  無論是無符號數還是表類型,當需要描述同一類型但數量不定的多個數據時。經常會使用一個前置的容量計數器加上若干個連續的數據項形式。因為Class的常量池中的常量是不固定的,所以常量池的入口需要前置一個容量計數器“contant_pool_count”用於記錄常量池中常量的數目,它是u2(2個字節)類型的。 用WinHex打開該Class文件獲得下圖:

 圖為Class文件的部分信息1

  該常量池的容量計數器的索引是從1開始的而不是從0開始,將0索引空出來時為了滿足后面某些指向常量池的索引值的數據在特定情況下需要表達“不引用任何一個常量”的意思。因為量池的容量計數器為u2,所以它的值為"0x0018",換算成10進制數為24,代表了常量池中有23個常量索引為1~23。為了驗證准確性,我們可以使用JDK為我們提供的javap工具,下圖為使用javap分析Class文件的部分截圖(只列出了常量池部分):

  可也從圖中的信息看出,該Class文件確實有23個常量。下面來具體分析常量池,常量池之中主要存放兩大類常量:字面量(Literal)和符號引用(Symbolic Reference)。字面量一般為“文本字符串”、“被聲明為final的常量值”等等。符號引用一般為“類和接口的全限定名(Fully Qualified Name)”、“字段的名稱和描述符(Descriptor)”和“方法的名稱和描述符”。

 

  常量池中的每一項常量都是一個表類型,在JDK6.0之前有11種結構的表數據,這11種表都有一個共同的特點,就是表開始的第一位是一個u1類型的標志位(tag,取值為1~12,標志2空缺),它代表了當前這個常量屬於那種常量類型,11種常量類型所代表的具體含義如下圖所示:

回頭查看“Class文件的部分信息1”圖,第一個常量,它的標志位為"0x07",轉換為10進制數為7,查看“常量池項目類型”表可以發現該常量屬於CONSTANT_Class_info類型,次類型的常量代表一個類或接口的符號引用。CONSTANT_Class_info的結構圖如下:

  tag標志位已經解釋過了,name_index是一個索引值,它指向常量池中一個CONSTANT_Utf8_info類型的常量,此常量代表了這個類(接口)的全限定名,查看“Class文件的部分信息1”圖,name_index的值為"0x0002",轉換為10進制數為2,指向常量池中第二個常量。繼續查看“Class文件的部分信息1”圖,查找第二個常量的標志為"0x01",轉換為10進制數為1,查看常量池項目類型表可知確實是一個CONSTANT_Utf8_info類型的常量。 CONSTANT_Utf8_info類型的常量結構:

 length表示這個UTF-8編碼的字符串長度是多少字節,它后面緊跟着長度為length的UTF-8編碼方式的字符串。查看“Class文件的部分信息1”圖,length的值為"0x0026",轉換成10進制數為38,表示后面緊跟着長度為38個字節長度的字符串,參看“ Class文件的部分信息2”圖:

圖為Class文件的部分信息2

  查看圖片的右邊,可以看到被選中的38個字節長度的值為:com/beliefbetrayal/clazz/ClassFileTest這正是我們示例類的全限定名。分析了這么多,我們現在查看用javap工具分析出來的常量池信息(查看常量池信息),可以看到“constant #1 = class    #2”這條信息指明了該常量指向#2常量,“constant #2 = Asciz   com/beliefbetrayal/clazz/ClassFileTest”這條信息指明了該常量的值為“com/beliefbetrayal/clazz/ClassFileTest”可以看到我們的分析都是正確的。

  根據“Class文件的部分信息2”圖第三個常量的標志位為"0x07"轉換為10進制為7,查看“常量池項目類型”表可以發現該常量屬於CONSTANT_Class_info類型,次類型的常量代表一個類或接口的符號引用。根據“CONSTANT_Class_info的結構圖”,name_index值為"0x0004",轉換為10進制數為4,表示指向第四個常量。 根據“Class文件的部分信息2”圖第四個常量的標志為"0x01"轉換為10進制數為1,查看常量池項目類型表可知是一個CONSTANT_Utf8_info類型的常量,根據CONSTANT_Utf8_info類型結構圖,length的值為"0x0010"轉換為10進制數為16,表示后面緊跟着16個字節長度的字符串,如下圖:

  圖為Class文件的部分信息3 

  查看圖片的右邊,可以看到被選中的16個字節長度的值為:java/lang/Object。 我們現在再回去查看用javap工具分析出來的常量池信息(查看常量池信息),可以很開心的看到我們的分析又被驗證了^_^。再接再厲,分析第5個常量,它的標志為"0x01",轉換為10進制數為1 查看常量池項目類型表可知是一個CONSTANT_Utf8_info類型的常量,根據CONSTANT_Utf8_info類型結構圖,length的值為"0x0001"轉換為10進制數為1,表示后面緊跟着1個字節長度的字符串,如下圖:

 

  圖為Class文件的部分信息4 

查看圖片的右邊,可以看到被選中的1個字節長度的值為:“m”它是我們定義的成員變量的名稱。到此為止我們分析了ClassFileTest常量池中23個常量中的5個,其余的常量都可以使用類似的方法計算出來。手工推導出來后與javap工具分析出來的信息對比可以驗證推導的正確性。下面將常量池中的11種常量類型的結構表列出來:

 

  查看用javap工具分析出來的常量池信息(查看常量池信息,仔細觀察會發現有許多常量出現的莫名其妙,如:“I”, “LineNumberTable”等,這些自動生成的常量會被后面字段表,方法表和屬性表引用,他們會被用來描述一些不方便使用“固定字節”來表達的內容。

  常量池的介紹結束了,我們不需要將每一個常量都手工計算出來,主要是要掌握好方法,JDK已經為我們提供了功能強大的Class文件分析工具javap。 



免責聲明!

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



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