常量池在JVM中分為三種:字符串常量池费薄,運(yùn)行時(shí)常量池后德,Class常量池
討論范圍 JDK 1.7版本及以后
字符串常量池(String pool)
(1)什么是字符串常量池部宿?
①字符串常量由一個(gè)個(gè)字符組成。在HotSpot虛擬機(jī)中,字符串常量池是有一個(gè)名為 StringTable 的類來實(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ì)象暂幼。一般我們說一個(gè)字符串進(jìn)入了全局的字符串常量池其實(shí)是說在這個(gè)StringTable中保存了對(duì)它的引用,反之移迫,如果說沒有在其中就是說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ù)來指定:-XX:StringTableSize=123456
(2)字符串常量池保存在JVM哪個(gè)區(qū)域徽龟?
在JDK1.6及之前的版本中,字符串常量池是放在永久代(Perm Gen)中唉地,確切來講是永久代的方法區(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è)。我們常說的常量池隆圆,就是指方法區(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í)常量池。也就是說運(yùn)行時(shí)常量池是class常量池的另一個(gè)版本岁经。
但是朋沮,這個(gè)“進(jìn)入”過程,并不會(huì)直接把所有類中定義的常量全部都加載進(jìn)來缀壤,而是會(huì)做個(gè)比較樊拓,如果需要加到字符串常量池中的字符串已經(jīng)存在,那么就不需要再把字符串字面量加載進(jìn)來了塘慕。
推薦閱讀
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