柚子快報(bào)邀請(qǐng)碼778899分享:聊聊JVM中的幾種常量池
柚子快報(bào)邀請(qǐng)碼778899分享:聊聊JVM中的幾種常量池
目錄
Class文件常量池
運(yùn)行時(shí)常量池
字符串常量池
對(duì)象池
在Java虛擬機(jī)(JVM)的世界中,常量池是一個(gè)備受矚目的話題。它不僅是內(nèi)存管理的關(guān)鍵組成部分,還直接影響著程序的性能和資源利用率。在本文中,我們將探討Java中幾種主要常量池的實(shí)現(xiàn)方式,以及它們?cè)诓煌姹綣DK中的變化和優(yōu)化。
Class文件常量池
當(dāng)Java源文件經(jīng)過(guò)編譯后,會(huì)生成對(duì)應(yīng)的Class文件。
在Class文件中,開(kāi)頭的4個(gè)字節(jié)用于存儲(chǔ)魔數(shù)(Magic Number),用于驗(yàn)證文件是否為JVM可接受的格式;緊接著的4個(gè)字節(jié)用于存儲(chǔ)版本號(hào),其中前2個(gè)字節(jié)表示次版本號(hào),后2個(gè)字節(jié)表示主版本號(hào);常量池緊隨其后,用于存放常量。在Java虛擬機(jī)加載Class文件時(shí),這些信息會(huì)被存儲(chǔ)在方法區(qū)。
常量池主要包括兩種類型的常量:字面量,類似于Java語(yǔ)言層面的常量,例如文本字符串和聲明為final的常量值;符號(hào)引用,屬于編譯原理的概念。通過(guò)javap命令生成更可讀的JVM字節(jié)碼指令文件:
字面量
在Java中,字面量指的是字符串常量或數(shù)值常量,它們只能作為右值出現(xiàn),也就是等號(hào)右邊的值。例如,在表達(dá)式 int a=1 中,1 就是字面量。
符號(hào)引用
????? 符號(hào)引用是編譯原理中的概念,相對(duì)于直接引用而言。主要包括三類常量:類和接口的全限定名,字段名稱和描述符,方法名稱和描述符。舉例來(lái)說(shuō),在之前提到的表達(dá)式中,a 就是一個(gè)字段名稱,也是一種符號(hào)引用。
運(yùn)行時(shí)常量池
運(yùn)行時(shí)常量池也是方法區(qū)的一部分。當(dāng)Class文件被加載到內(nèi)存后,Java虛擬機(jī)會(huì)將其中的內(nèi)容轉(zhuǎn)移到運(yùn)行時(shí)常量池中,對(duì)應(yīng)的符號(hào)引用會(huì)被轉(zhuǎn)變?yōu)閮?nèi)存中代碼的直接引用,這就是動(dòng)態(tài)鏈接。
由于Java語(yǔ)言并不要求常量只能在編譯期產(chǎn)生,因此不是只有預(yù)置入Class文件中常量池的內(nèi)容才能進(jìn)入方法區(qū)的運(yùn)行時(shí)常量池,在運(yùn)行期間,也可以將新的常量放入池中。
字符串常量池
設(shè)計(jì)思想
在我們的工作中,String類是一種極其常用的對(duì)象類型,因此為了提高性能和節(jié)省內(nèi)存,JVM維護(hù)了一個(gè)特殊的內(nèi)存空間,即字符串常量池。這類似于緩存區(qū),在創(chuàng)建字符串常量時(shí),首先會(huì)檢查字符串常量池中是否存在該字符串,如果存在則返回引用實(shí)例,如果不存在,則會(huì)在常量池中創(chuàng)建一個(gè)新對(duì)象,再返回引用。
但是,值得注意的是,字符串池的實(shí)現(xiàn)前提是String對(duì)象是不可變的。這樣可以確保多個(gè)引用同時(shí)指向字符串池中的同一個(gè)對(duì)象。如果字符串是可變的,一個(gè)引用的操作改變了對(duì)象的值,會(huì)對(duì)其他引用產(chǎn)生影響,這是不合理的。
字符串創(chuàng)建
在創(chuàng)建字符串對(duì)象時(shí),有幾種不同的方式。
字面量方式
使用字面量方式創(chuàng)建的字符串對(duì)象只會(huì)存在于常量池中。在創(chuàng)建對(duì)象時(shí),JVM會(huì)首先在常量池中通過(guò)equals(key)方法判斷是否存在相同的對(duì)象。如果存在,則直接返回該對(duì)象在常量池中的引用;如果不存在,則會(huì)在常量池中創(chuàng)建一個(gè)新對(duì)象,再返回引用。
new String()
另一種方式是使用new String()關(guān)鍵字,這會(huì)在堆中創(chuàng)建一個(gè)對(duì)象,同時(shí)也會(huì)存在于字符串常量池中。
JVM首先會(huì)在字符串常量池中查找是否存在相同的字符串對(duì)象,如果有,則不在池中再去創(chuàng)建,而是直接在堆中創(chuàng)建一個(gè)對(duì)象,然后返回堆中對(duì)象的引用。如果沒(méi)有,則首先在字符串常量池中創(chuàng)建一個(gè)字符串對(duì)象,然后再在堆中創(chuàng)建一個(gè)相同的對(duì)象,最后返回堆中對(duì)象的引用。
intern()
另外,還有一個(gè)方法是使用intern()方法。這是一個(gè)本地方法,調(diào)用intern()方法時(shí),如果字符串在字符串常量池中存在對(duì)應(yīng)的字面量,則返回該字面量的地址;如果不存在,則創(chuàng)建一個(gè)對(duì)應(yīng)的字面量,并返回其地址。
字符串常量池位置變遷
Jdk1.6及以前運(yùn)行時(shí)常量池在永久代,其中包含字符串常量池; Jdk1.7字符串常量池從永久代里的運(yùn)行時(shí)常量池分離到堆中; Jdk1.8及之后運(yùn)行時(shí)常量池在元空間,字符串常量池里仍然在堆里;。
總結(jié)
?????字符串常量池的存在避免了頻繁創(chuàng)建相同內(nèi)容的字符串,從而節(jié)省了內(nèi)存,并減少了創(chuàng)建相同字符串耗費(fèi)的時(shí)間,提升了性能。同時(shí)字符串常量池犧牲了在常量池中遍歷對(duì)象所需的時(shí)間,不過(guò)這種時(shí)間成本相對(duì)較低。
對(duì)象池
對(duì)于Java中基本類型的包裝類,大部分都實(shí)現(xiàn)了常量池技術(shù)(嚴(yán)格來(lái)說(shuō)應(yīng)該稱為對(duì)象池,在堆上),包括Byte、Short、Integer、Long、Character、Boolean,而另外兩種浮點(diǎn)數(shù)類型的包裝類則沒(méi)有實(shí)現(xiàn)。
此外,Byte、Short、Integer、Long、Character這五種整型的包裝類只有在對(duì)應(yīng)值小于等于127時(shí)才可以使用對(duì)象池,也就是說(shuō),對(duì)象池不負(fù)責(zé)創(chuàng)建和管理大于127的這些類的對(duì)象。這是因?yàn)橐话銇?lái)說(shuō),這些較小的數(shù)值被使用的概率相對(duì)較大。如果超出了對(duì)應(yīng)范圍,仍然會(huì)去創(chuàng)建新的對(duì)象。
歡迎關(guān)注微信訂閱號(hào):技術(shù)勘察館
柚子快報(bào)邀請(qǐng)碼778899分享:聊聊JVM中的幾種常量池
相關(guān)鏈接
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。