常量池在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