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