JVM之類文件結構——上篇(常量池)

Java語言之所以強大的一個原因就在於它具有跨平臺性,Java源文件被編譯後的結果不是本地機器碼(與機器平臺相關聯)而是字節碼(與機器平臺無關),然後由JVM將字節碼去加載並執行它。
那麼Java的字節碼文件到底長什麼樣?
下面就是我們程序員入門的HelloWorld程序的Class文件
源文件

public class HelloWorld{
    public static void main(String[] args){
        System.out.println("Hello World");
    }
}

Class文件
這裏寫圖片描述
這裏我是用的WinHex軟件打開的,把在磁盤上的文件裏的二進制轉化爲十六進制顯示出來了
大家可能看的一頭霧水,這到底是是什麼呀,全是一些數字,完全不知道什麼意思,先不要着急,前面的圖大概認識一下就行了,接下來我們對照着Java虛擬機規範,就很容易理解每一個數字代表的含義了。
首先我們看前四個字節,也就是下圖:
這裏寫圖片描述
我們可以看到,是0xCAFEBABE,這個稱之爲魔數,每一個class文件都會有這個東西,這個數字非常有意思,就是咖啡寶貝的意思,非常容易記憶,那麼這個數字有什麼作用,其實就是一個標誌,如果一個文件的頭部有了這個0xCAFEBABE這個信息,我就認爲你是一個class文件。
接下來的四個字節就是版本號了,前面的兩個字節是次版本號,後面的兩個字節是主版本號
這裏寫圖片描述
我們可以看到,次版本號的值是0x0000,而主版本號是0x0033,這裏使用的是大端法(低地址在高位[和人類閱讀的習慣相同]),所以,十六進制的0033就是十進制的51,這個對應的編譯器(JDK中的javac)版本是JDK 1.7.0,不同版本的編譯器編譯出來的class文件中的版本號不一樣。其實這個信息就表示了你所使用的的編譯器版本。【高版本的JDK兼容低版本的JDK】
接下來的兩個字節是常量池容量
這裏寫圖片描述
我們可以看到,常量池容量的值爲34(十六進制的0x22),這代表着常量池中有33個常量這裏要特別注意不是34個,常量的索引從1開始,索引值範圍爲1-33,爲什麼把第0項空出來呢?這是有原因的,這是因爲如果有某些指向常量池的索引它想表達“不引用任何常量池項目”,就把它的索引值置爲0
接下來的內容就比較重要了,接下來的就是常量池,常量池,顧名思義,裏面裝的都是常量,都有哪些常量?
總體來說,常量池主要存放了兩大類常量:字面量符號引用
這裏寫圖片描述
1、什麼是字面量?字面量是用於表達源代碼中的一個固定值的一個標識,例如:String s = “abc”, 字符串abc就是Java字面量
2、什麼是符號引用? 符號引用簡單地來說就是一個字符串,該字符串指向了被引用的事物,其實就和人的身份證號一樣,你的這個字符串身份證號碼就指向了你,比如說Java中一個類中定義了另一個類,那個這個類就具有另一個類的符號引用,當JVM在加載這個類的時候,就會在常量池中獲得另一個類的符號引用,在類創建或運行時解析、翻譯到具體的內存地址之中。【符號引用和具體的內存佈局沒有關係】
3、什麼是描述符,簡單來說就是變量、方法的描述符號,比如 int i = 3; 這個int類型我就用一個符號去描述它
4、什麼是全限定名,在Java源文件中可以簡單理解爲就是包名+類名,比如: com.coderising.jvm.loader.HelloWorld,而在Class文件中,只不過把”.”換成了”/”,如:com/coderising/jvm/loader/HelloWorld
常量池中有許多種常量項,每一個常量項都是一個數據結構。
例如CONSTANT_Class_info
這裏寫圖片描述
我們結合Class文件來理解一下常量項【u1代表佔一個字節,u2代表佔兩個字節】
這裏寫圖片描述
虛擬機在讀到0x07這個字節的時候,查一下表【虛擬機規範有這個表,在虛擬機規範中規定了所有的常量池結構】,哦,原來這是個CONSTANT_Class_info常量項,然後就會往後讀兩個字節(虛擬機規範中就規定了這個數據結構),根據虛擬機規範,虛擬機就會知道這兩個字節的值代表的是指向一個全限定名[簡單地理解爲包名+類名]的索引,很顯然,這裏這個索引的值爲2,就是說,我指向了第二個常量項(其實就是下一個常量項)注意:這裏的 07 00 02 這三個字節是一個整體,這三個字節符合CONSTANT_Class_info數據結構,這個整體是一個常量項,並且是第一個常量項。
下來我們再看看在常量池用頻繁出現的CONSTANT_UTF8_info常量項,它的數據結構如下:
這裏寫圖片描述
結合Class文件理解一下:
這裏寫圖片描述
注意:第二個常量項是圖中所有淺藍色的部分,其中包含tag(值爲0x01)、length(值爲0x0024)和後面的36個字節。這和CONSTANT_UTF8_info的數據結構是對應的。這個時候我們可以看到,其實第一個CONSTANT_Class_info的索引值是指向第二個常量項的,這個常量項的內容其實就是com/coderising/jvm/loader/HelloWorld,其實就是包名+類名。我們可以通過這種查表的方式,找到Class文件中的所有數字所代表的含義,詳情請參考《深入理解Java虛擬機》第六章P172中的表6-6。
我們看一看最後一個常量項:
這裏寫圖片描述
最後一個常量項一般爲這個Java源文件的名字。很顯然這是一個UTF8Info數據結構。
大家可以看到,Java源文件中的所有的字面量和符號引用都被存在了這個Class文件之中了
比如說,符號引用: com.coderising.jvm.loader.HelloWorld,還有我們在程序中要輸出的字面量“Hello World”
下來我們用javap -verbose 類名來輸出一下我們這個HelloWorld.class
這裏寫圖片描述
好了,常量池就聊到這裏

本文參考《深入理解Java虛擬機》周志明 著

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章