[last updated:2014/11/27]前端
個人上一篇文章《Java虛擬機原理圖解》 一、class文件基本組織結構中已經提到了class的文件結構,在class文件裏的魔數、副版本號號、主版本號以後,緊接着就是常量池的數據區域了,例如如下圖用紅線包含的位置:java
知道了常量池的位置後,而後讓咱們來揭祕常量池裏到底有什麼東西吧~ 數組
常量池的組織很是easy,前端的兩個字節佔有的位置叫作常量池計數器(constant_pool_count),它記錄着常量池的組成元素 常量池項(cp_info) 的個數。緊接着會排列着constant_pool_count-1個常量池項(cp_info)。例如如下圖所看到的:jvm
每個常量池項(cp_info) 都會相應記錄着class文件裏的某中類型的字面量。讓咱們先來了解一下常量池項(cp_info)的結構吧:工具
JVM虛擬機規定了不一樣的tag值和不一樣類型的字面量相應關係例如如下:ui
因此依據cp_info中的tag 不一樣的值,可以將cp_info 更細化爲下面結構體:編碼
CONSTANT_Utf8_info,CONSTANT_Integer_info,CONSTANT_Float_info,CONSTANT_Long_info,
CONSTANT_Double_info,CONSTANT_Class_info,CONSTANT_String_info,CONSTANT_Fieldref_info,
CONSTANT_Methodref_info,CONSTANT_InterfaceMethodref_info,CONSTANT_NameAndType_info,CONSTANT_MethodHandle_info,
CONSTANT_MethodType_info,CONSTANT_InvokeDynamic_info。lua
現在讓咱們看一下細化了的常量池的結構會是相似下圖所看到的的樣子:spa
Java語言規範規定了 int類型和Float 類型的數據類型佔用 4 個字節的空間。那麼存在於class字節碼文件裏的該類型的常量是怎樣存儲的呢?對應地,在常量池中,將 int和Float類型的常量分別使用CONSTANT_Integer_info和 Constant_float_info表示,他們的結構例如如下所看到的:.net
舉例:建如下的類 IntAndFloatTest.java,在這個類中,咱們聲明瞭五個變量,但是取值就兩種int類型的10 和Float類型的11f。
package com.louis.jvm; public class IntAndFloatTest { private final int a = 10; private final int b = 10; private float c = 11f; private float d = 11f; private float e = 11f; }
而後用編譯器編譯成IntAndFloatTest.class字節碼文件,咱們經過javap -v IntAndFloatTest 指令來看一下其常量池中的信息,可以看到儘管咱們在代碼中寫了兩次10 和三次11f,但是常量池中,就僅僅有一個常量10 和一個常量11f,例如如下圖所看到的:
從結果上可以看到常量池第#8 個常量池項(cp_info) 就是CONSTANT_Integer_info,值爲10;第#23個常量池項(cp_info) 就是CONSTANT_Float_info,值爲11f。(常量池中其它的東西先別糾結啦,咱們會面會一一解說的哦)。
代碼中所有用到 int 類型 10 的地方,會使用指向常量池的指針值#8 定位到第#8 個常量池項(cp_info),即值爲 10的結構體 CONSTANT_Integer_info,而用到float類型的11f時,也會指向常量池的指針值#23來定位到第#23個常量池項(cp_info) 即值爲11f的結構體CONSTANT_Float_info。例如如下圖所看到的:
Java語言規範規定了 long 類型和 double類型的數據類型佔用8 個字節的空間。那麼存在於class 字節碼文件裏的該類型的常量是怎樣存儲的呢?對應地,在常量池中,將long和double類型的常量分別使用CONSTANT_Long_info和Constant_Double_info表示,他們的結構例如如下所看到的:
舉例:建如下的類 LongAndDoubleTest.java,在這個類中,咱們聲明瞭六個變量,但是取值就兩種Long 類型的-6076574518398440533L 和Double 類型的10.1234567890D。
package com.louis.jvm; public class LongAndDoubleTest { private long a = -6076574518398440533L; private long b = -6076574518398440533L; private long c = -6076574518398440533L; private double d = 10.1234567890D; private double e = 10.1234567890D; private double f = 10.1234567890D; }
而後用編譯器編譯成 LongAndDoubleTest.class 字節碼文件,咱們經過javap -v LongAndDoubleTest指令來看一下其常量池中的信息,可以看到儘管咱們在代碼中寫了三次-6076574518398440533L 和三次10.1234567890D,但是常量池中,就僅僅有一個常量-6076574518398440533L 和一個常量10.1234567890D,例如如下圖所看到的:
從結果上可以看到常量池第 #18 個常量池項(cp_info) 就是CONSTANT_Long_info,值爲-6076574518398440533L ;第 #26個常量池項(cp_info) 就是CONSTANT_Double_info,值爲10.1234567890D。(常量池中其它的東西先別糾結啦,咱們會面會一一解說的哦)。
代碼中所有用到 long 類型-6076574518398440533L 的地方,會使用指向常量池的指針值#18 定位到第 #18 個常量池項(cp_info),即值爲-6076574518398440533L 的結構體CONSTANT_Long_info,而用到double類型的10.1234567890D時,也會指向常量池的指針值#26 來定位到第 #26 個常量池項(cp_info) 即值爲10.1234567890D的結構體CONSTANT_Double_info。例如如下圖所看到的:
對於字符串而言,JVM會將字符串類型的字面量以UTF-8 編碼格式存儲到在class字節碼文件裏。這麼說可能有點摸不着北,咱們先從直觀的Java源代碼中中出現的用雙引號"" 括起來的字符串來看,在編譯器編譯的時候,都會將這些字符串轉換成CONSTANT_String_info結構體,而後放置於常量池中。其結構例如如下所看到的:
如上圖所看到的的結構體,CONSTANT_String_info結構體中的string_index的值指向了CONSTANT_Utf8_info結構體,而字符串的utf-8編碼數據就在這個結構體之中。例如如下圖所看到的:
請看一例,定義一個簡單的StringTest.java類,而後在這個類里加一個"JVM原理" 字符串,而後,咱們來看看它在class文件裏是如何組織的。
package com.louis.jvm; public class StringTest { private String s1 = "JVM原理"; private String s2 = "JVM原理"; private String s3 = "JVM原理"; private String s4 = "JVM原理"; }將Java源代碼編譯成StringTest.class文件後,在此文件的文件夾下運行 javap -v StringTest 命令,會看到例如如下的常量池信息的輪廓:
(PS :使用javap -v 指令能看到易於咱們閱讀的信息,查看真正的字節碼文件可以使用HEXWin、NOTEPAD++、UtraEdit 等工具。)
在面的圖中,咱們可以看到CONSTANT_String_info結構體位於常量池的第#15個索引位置。而存放"Java虛擬機原理" 字符串的 UTF-8編碼格式的字節數組被放到CONSTANT_Utf8_info結構體中,該結構體位於常量池的第#16個索引位置。上面的圖僅僅是看了個輪廓,讓咱們再深刻地看一下它們的組織吧。請看下圖:
由上圖可見:「JVM原理」的UTF-8編碼的數組是:4A564D E5 8E 9FE7 90 86,並且存入了CONSTANT_Utf8_info結構體中。
JVM會將某個Java 類中所有使用到了的類的全然限定名 以二進制形式的全然限定名 封裝成CONSTANT_Class_info結構體中,而後將其放置到常量池裏。CONSTANT_Class_info 的tag值爲 7 。其結構例如如下:
舉例,咱們定義一個很是easy的ClassTest類,來看一下常量池是怎麼對類的全然限定名進行存儲的。
package com.jvm; import java.util.Date; public class ClassTest { private Date date =new Date(); }將Java源代碼編譯成ClassTest.class文件後,在此文件的文件夾下運行 javap -v ClassTest 命令,會看到例如如下的常量池信息的輪廓:
如上圖所看到的,在ClassTest.class文件的常量池中,共同擁有 3 個CONSTANT_Class_info結構體,分別表示ClassTest 中用到的Class信息。 咱們就看當中一個表示com/jvm.ClassTest的CONSTANT_Class_info 結構體。它在常量池中的位置是#1,它的name_index值爲#2,它指向了常量池的第2 個常量池項,例如如下所看到的:
-----------------------------------------------------------------------------------------------------------------------------------------
本文源自 http://blog.csdn.net/luanlouis/,如需轉載,請註明出處,謝謝!