Java中几种常量池的区分
轉(zhuǎn)載自??Java中幾種常量池的區(qū)分
在java的內(nèi)存分配中,經(jīng)常聽(tīng)到很多關(guān)于常量池的描述,我開(kāi)始看的時(shí)候也是看的很模糊,網(wǎng)上五花八門(mén)的說(shuō)法簡(jiǎn)直太多了,最后查閱各種資料,終于算是差不多理清了,很多網(wǎng)上說(shuō)法都有問(wèn)題,筆者嘗試著來(lái)區(qū)分一下這幾個(gè)概念。
1.全局字符串池(string pool也有叫做string literal pool)
全局字符串池里的內(nèi)容是在類(lèi)加載完成,經(jīng)過(guò)驗(yàn)證,準(zhǔn)備階段之后在堆中生成字符串對(duì)象實(shí)例,然后將該字符串對(duì)象實(shí)例的引用值存到string pool中(記住:string pool中存的是引用值而不是具體的實(shí)例對(duì)象,具體的實(shí)例對(duì)象是在堆中開(kāi)辟的一塊空間存放的。)。 在HotSpot VM里實(shí)現(xiàn)的string pool功能的是一個(gè)StringTable類(lèi),它是一個(gè)哈希表,里面存的是駐留字符串(也就是我們常說(shuō)的用雙引號(hào)括起來(lái)的)的引用(而不是駐留字符串實(shí)例本身),也就是說(shuō)在堆中的某些字符串實(shí)例被這個(gè)StringTable引用之后就等同被賦予了”駐留字符串”的身份。這個(gè)StringTable在每個(gè)HotSpot VM的實(shí)例只有一份,被所有的類(lèi)共享。
2.class文件常量池(class constant pool)
我們都知道,class文件中除了包含類(lèi)的版本、字段、方法、接口等描述信息外,還有一項(xiàng)信息就是常量池(constant pool table),用于存放編譯器生成的各種字面量(Literal)和符號(hào)引用(Symbolic References)。 字面量就是我們所說(shuō)的常量概念,如文本字符串、被聲明為final的常量值等。 符號(hào)引用是一組符號(hào)來(lái)描述所引用的目標(biāo),符號(hào)可以是任何形式的字面量,只要使用時(shí)能無(wú)歧義地定位到目標(biāo)即可(它與直接引用區(qū)分一下,直接引用一般是指向方法區(qū)的本地指針,相對(duì)偏移量或是一個(gè)能間接定位到目標(biāo)的句柄)。一般包括下面三類(lèi)常量:
- 類(lèi)和接口的全限定名
- 字段的名稱(chēng)和描述符
- 方法的名稱(chēng)和描述符
常量池的每一項(xiàng)常量都是一個(gè)表,一共有如下表所示的11種各不相同的表結(jié)構(gòu)數(shù)據(jù),這每個(gè)表開(kāi)始的第一位都是一個(gè)字節(jié)的標(biāo)志位(取值1-12),代表當(dāng)前這個(gè)常量屬于哪種常量類(lèi)型。?每種不同類(lèi)型的常量類(lèi)型具有不同的結(jié)構(gòu),具體的結(jié)構(gòu)本文就先不敘述了,本文著重區(qū)分這三個(gè)常量池的概念(讀者若想深入了解每種常量類(lèi)型的數(shù)據(jù)結(jié)構(gòu)可以查看《深入理解java虛擬機(jī)》第六章的內(nèi)容)。
3.運(yùn)行時(shí)常量池(runtime constant pool)
當(dāng)java文件被編譯成class文件之后,也就是會(huì)生成我上面所說(shuō)的class常量池,那么運(yùn)行時(shí)常量池又是什么時(shí)候產(chǎn)生的呢?
jvm在執(zhí)行某個(gè)類(lèi)的時(shí)候,必須經(jīng)過(guò)加載、連接、初始化,而連接又包括驗(yàn)證、準(zhǔn)備、解析三個(gè)階段。而當(dāng)類(lèi)加載到內(nèi)存中后,jvm就會(huì)將class常量池中的內(nèi)容存放到運(yùn)行時(shí)常量池中,由此可知,運(yùn)行時(shí)常量池也是每個(gè)類(lèi)都有一個(gè)。在上面我也說(shuō)了,class常量池中存的是字面量和符號(hào)引用,也就是說(shuō)他們存的并不是對(duì)象的實(shí)例,而是對(duì)象的符號(hào)引用值。而經(jīng)過(guò)解析(resolve)之后,也就是把符號(hào)引用替換為直接引用,解析的過(guò)程會(huì)去查詢(xún)?nèi)肿址?#xff0c;也就是我們上面所說(shuō)的StringTable,以保證運(yùn)行時(shí)常量池所引用的字符串與全局字符串池中所引用的是一致的。
舉個(gè)實(shí)例來(lái)說(shuō)明一下:
| 1 2 3 4 5 6 7 8 | String str1 = "abc"; String str2 = new String("def"); String str3 = "abc"; String str4 = str2.intern(); String str5 = "def"; System.out.println(str1 == str3);//true System.out.println(str2 == str4);//false System.out.println(str4 == str5);//true |
上面程序的首先經(jīng)過(guò)編譯之后,在該類(lèi)的class常量池中存放一些符號(hào)引用,然后類(lèi)加載之后,將class常量池中存放的符號(hào)引用轉(zhuǎn)存到運(yùn)行時(shí)常量池中,然后經(jīng)過(guò)驗(yàn)證,準(zhǔn)備階段之后,在堆中生成駐留字符串的實(shí)例對(duì)象(也就是上例中str1所指向的”abc”實(shí)例對(duì)象),然后將這個(gè)對(duì)象的引用存到全局String Pool中,也就是StringTable中,最后在解析階段,要把運(yùn)行時(shí)常量池中的符號(hào)引用替換成直接引用,那么就直接查詢(xún)StringTable,保證StringTable里的引用值與運(yùn)行時(shí)常量池中的引用值一致,大概整個(gè)過(guò)程就是這樣了。
回到上面的那個(gè)程序,現(xiàn)在就很容易解釋整個(gè)程序的內(nèi)存分配過(guò)程了,首先,在堆中會(huì)有一個(gè)”abc”實(shí)例,全局StringTable中存放著”abc”的一個(gè)引用值,然后在運(yùn)行第二句的時(shí)候會(huì)生成兩個(gè)實(shí)例,一個(gè)是”def”的實(shí)例對(duì)象,并且StringTable中存儲(chǔ)一個(gè)”def”的引用值,還有一個(gè)是new出來(lái)的一個(gè)”def”的實(shí)例對(duì)象,與上面那個(gè)是不同的實(shí)例,當(dāng)在解析str3的時(shí)候查找StringTable,里面有”abc”的全局駐留字符串引用,所以str3的引用地址與之前的那個(gè)已存在的相同,str4是在運(yùn)行的時(shí)候調(diào)用intern()函數(shù),返回StringTable中”def”的引用值,如果沒(méi)有就將str2的引用值添加進(jìn)去,在這里,StringTable中已經(jīng)有了”def”的引用值了,所以返回上面在new str2的時(shí)候添加到StringTable中的 “def”引用值,最后str5在解析的時(shí)候就也是指向存在于StringTable中的”def”的引用值,那么這樣一分析之后,下面三個(gè)打印的值就容易理解了。
總結(jié)
- 1.全局常量池在每個(gè)VM中只有一份,存放的是字符串常量的引用值。
- 2.class常量池是在編譯的時(shí)候每個(gè)class都有的,在編譯階段,存放的是常量的符號(hào)引用。
- 3.運(yùn)行時(shí)常量池是在類(lèi)加載完成之后,將每個(gè)class常量池中的符號(hào)引用值轉(zhuǎn)存到運(yùn)行時(shí)常量池中,也就是說(shuō),每個(gè)class都有一個(gè)運(yùn)行時(shí)常量池,類(lèi)在解析之后,將符號(hào)引用替換成直接引用,與全局常量池中的引用值保持一致。
總結(jié)
以上是生活随笔為你收集整理的Java中几种常量池的区分的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 电脑共享文件夹如何共享电脑里的文件
- 下一篇: 台式机电脑开机后显示器无信号什么原因台式