深入淺出JVM常量池

常量池在JVM中分為三種:字符串常量池,運(yùn)行時(shí)常量池,Class常量池
討論范圍 JDK 1.7版本及以后

字符串常量池(String pool)
(1)什么是字符串常量池?
①字符串常量由一個(gè)個(gè)字符組成。在HotSpot虛擬機(jī)中,字符串常量池是有一個(gè)名為 StringTable 的類來(lái)實(shí)現(xiàn)的,StringTable是一個(gè)哈希表HashSet<String>,默認(rèn)長(zhǎng)度為1009,是被所有類共享的,因此在JVM中的實(shí)例只有一份。注意它只存儲(chǔ)對(duì)java.lang.String實(shí)例的引用,而不存儲(chǔ)String對(duì)象的內(nèi)容,根據(jù)這個(gè)引用可以得到具體的String對(duì)象。一般我們說(shuō)一個(gè)字符串進(jìn)入了全局的字符串常量池其實(shí)是說(shuō)在這個(gè)StringTable中保存了對(duì)它的引用,反之,如果說(shuō)沒有在其中就是說(shuō)StringTable中沒有對(duì)它的引用。

②在JDK1.6版本中,StringTable的長(zhǎng)度是固定的,長(zhǎng)度就是1009。因此如果保存的字符串常量過多,就會(huì)造成哈希沖突,導(dǎo)致鏈表過長(zhǎng),而鏈表過長(zhǎng)的直接影響就是 當(dāng)調(diào)用String.intern時(shí)性能會(huì)大幅下降(因?yàn)橐粋€(gè)一個(gè)找)。

③在JDK1.7版本中,StringTable的長(zhǎng)度可以通過參數(shù)來(lái)指定:-XX:StringTableSize=123456

(2)字符串常量池保存在JVM哪個(gè)區(qū)域?
在JDK1.6及之前的版本中,字符串常量池是放在永久代(Perm Gen)中,確切來(lái)講是永久代的方法區(qū)中。
在JDK1.7后的版本中,字符串常量池就被移到了堆(Heap)中。之所以會(huì)被移到堆中,可能是方法區(qū)的內(nèi)存空間太小了。

(3)字符串常量池中保存的是什么?
字符串常量池只存儲(chǔ)引用,不存儲(chǔ)內(nèi)容!字符串不是存在字符串常量池中而是存在堆內(nèi)存中,字符串池中只是該字符串的引用。
規(guī)范里把存儲(chǔ)Java對(duì)象的地方定義為Java heap,其它地方是不會(huì)存有Java對(duì)象的實(shí)體的(有的話那根據(jù)定于也要算Java heap的一部分)

注:在字符串常量池中的字符串只能存在一份。

String s1 ="JVM";
String s2 ="JVM";
//執(zhí)行完第一段代碼后,由于常量池中已經(jīng)存在了"JVM"這個(gè)字符串的引用,
//所以s2不會(huì)在常量池中申請(qǐng)新的空間,而是直接把已存在的字符串內(nèi)存地址返回給s2。

Class常量池
(1)什么是Class常量池?
①在每一個(gè)Java類被編譯后,會(huì)形成一個(gè)class文件。Class文件除了有類信息,字段,方法,接口等描述信息,還有一項(xiàng)信息是常量池(Constant Pool Table),每一個(gè)class文件都有一個(gè)class常量池。

(2)Class常量池保存在JVM哪個(gè)區(qū)域?
保存在堆中。

(3)Class常量池中保存的是什么?
用于存放編譯器生成的各種字面量(Literal)和符號(hào)引用(Symbolic References);
字面量(Literal)包括:文本字符串,八種基本類型的值,被聲明為final的常量等
符號(hào)引用(Symbolic References)包括:類和接口的全限定名(Full Qualified Name),字段的名稱和描述符(Descriptor),方法的名稱和描述符

運(yùn)行時(shí)常量池(Runtime Constant Pool)
(1)什么是運(yùn)行時(shí)常量池?
運(yùn)行時(shí)常量池,則是jvm虛擬機(jī)在完成類裝載操作后,將class文件中的常量池載入到內(nèi)存中,并保存在方法區(qū)中。 運(yùn)行時(shí)常量池也就是class常量池被加載到內(nèi)存之后的版本,因?yàn)槊恳粋€(gè)class文件都有一個(gè)class常量池,因此運(yùn)行時(shí)常量池也是每個(gè)類都有一個(gè)。我們常說(shuō)的常量池,就是指方法區(qū)中的運(yùn)行時(shí)常量池。

運(yùn)行時(shí)常量池相對(duì)于Class文件常量池的重要特征是具有動(dòng)態(tài)性。除了在編譯器產(chǎn)生的常量可以進(jìn)入運(yùn)行時(shí)常量池外,還可以在運(yùn)行期間將新的常量放入池中,比如String 的 intern方法。

(2)運(yùn)行時(shí)常量池保存在JVM哪個(gè)區(qū)域?
位于方法區(qū),Java虛擬機(jī)規(guī)范中將方法區(qū)描述為堆的邏輯部分,所以實(shí)際上還是在堆中。

(3)Class常量池中保存的是什么?
方法區(qū)的一部分。Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有常量池(Constant Pool Table),存放編譯期生成的各種字面量和符號(hào)引用,這部分內(nèi)容將在類加載后進(jìn)入方法區(qū)的運(yùn)行時(shí)常量池。也就是說(shuō)運(yùn)行時(shí)常量池是class常量池的另一個(gè)版本。

但是,這個(gè)“進(jìn)入”過程,并不會(huì)直接把所有類中定義的常量全部都加載進(jìn)來(lái),而是會(huì)做個(gè)比較,如果需要加到字符串常量池中的字符串已經(jīng)存在,那么就不需要再把字符串字面量加載進(jìn)來(lái)了。

推薦閱讀
https://www.zhihu.com/question/29884421
https://www.zhihu.com/question/57109429/answer/151717241
https://www.zhihu.com/question/55994121/answer/147296098
https://blog.csdn.net/zm13007310400/article/details/77534349

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • ??需要說(shuō)明的一點(diǎn)是,這篇文章是以《深入理解Java虛擬機(jī)》第二版這本書為基礎(chǔ)的,這里假設(shè)大家已經(jīng)了解了JVM的運(yùn)...
    Geeks_Liu閱讀 14,279評(píng)論 5 44
  • 這篇文章是我之前翻閱了不少的書籍以及從網(wǎng)絡(luò)上收集的一些資料的整理,因此不免有一些不準(zhǔn)確的地方,同時(shí)不同JDK版本的...
    高廣超閱讀 16,042評(píng)論 3 83
  • 本文跟大家聊聊JVM的內(nèi)部結(jié)構(gòu),從組件中的多線程處理,JVM系統(tǒng)線程,局部變量數(shù)組等方面進(jìn)行解析 JVM JVM ...
    認(rèn)真期待閱讀 550評(píng)論 0 1
  • 找了大半天,沒有合適的配圖。干脆就這樣純文字吧。月月在懷里熟睡讓我一點(diǎn)點(diǎn)找回被需要的感覺。 一直以來(lái),我并不容易被...
    久久桃花閱讀 288評(píng)論 0 1
  • 小燕感恩日記(第24天)2018.6.11 感恩,開啟豐盛富足的人生。 感恩今天花出去的錢寶寶為我換來(lái)美味...
    小燕十豐盛富足的人生閱讀 205評(píng)論 0 2

友情鏈接更多精彩內(nèi)容