java學(xué)習(xí)——淺談Java常量池

一莺奸、概述

  • 常量池:編譯期被確定佛呻,*.class文件中的一部分,包含字面量(Literal)和符號(hào)引用(Symbolic Reference)匪蝙。
  • 字面量:文本字符串、聲明為final的常量值(int/long/double...)等习贫。
  • 符號(hào)引用:類和接口的完全限定名(Fully Qualified Name)骗污、字段的名稱和描述符(Descriptor)、方法的名稱和描述符沈条。
  • 運(yùn)行時(shí)常量池:方法區(qū)的一部分需忿,jvm在完成類裝載操作后,將class文件中的常量池載入內(nèi)存并保存在方法區(qū)中蜡歹。
  • JDK1.6之前字符串常量池位于方法區(qū)屋厘。
    JDK1.7字符串常量池已經(jīng)被移至堆。
    JDK1.8字符串常量池位移至元空間月而。

二汗洒、字符串常量池

字符串常量池屬于運(yùn)行時(shí)常量池的一部分

String s1 = "Hello";
String s2 = "Hello";
String s3 = "Hel" + "lo";
String s4 = "Hel" + new String("lo");
String s5 = new String("Hello");
String s6 = s5.intern();
String s7 = "H";
String s8 = "ello";
String s9 = s7 + s8;
          
System.out.println(s1 == s2);  // true
System.out.println(s1 == s3);  // true
System.out.println(s1 == s4);  // false
System.out.println(s1 == s9);  // false
System.out.println(s4 == s5);  // false
System.out.println(s1 == s6);  // true

java中==比較的是內(nèi)存地址

  1. s1 == s2 (true),s1父款、s2賦值時(shí)均使用的字符串字面量"Hello"溢谤,在編譯期間,這種字面量會(huì)直接放入class文件的常量池中憨攒,載入運(yùn)行時(shí)常量池后世杀,s1、s2指向的是同一個(gè)內(nèi)存地址肝集。
  2. s1 == s3 (true)瞻坝,s3雖然是動(dòng)態(tài)拼接出來(lái)的字符串,但所有參與拼接的部分都是已知的字面量杏瞻,在編譯期間所刀,這種拼接會(huì)被優(yōu)化衙荐,因此String s3 = "Hel" + "lo";在class文件中被優(yōu)化成String s3 = "Hello";,所以s1 == s3成立浮创。
  3. s1 == s4 (false)忧吟,s4雖然也是拼接出來(lái)的,但new String("lo")這部分不是已知字面量斩披,編譯器不會(huì)優(yōu)化瀑罗,必須等到運(yùn)行時(shí)才可以確定結(jié)果,所以s4指向堆中某個(gè)地址雏掠。


    s4.jpg
  4. s1 == s9 (false),s9是s7劣像、s8兩個(gè)變量拼接乡话,都是不可預(yù)料的,編譯器不作優(yōu)化耳奕,運(yùn)行時(shí)拼接成新字符串存于堆中某個(gè)地址绑青。


    s9.png
  5. s4 == s5 (false),二者都在堆中屋群,但地址不同闸婴。
  6. s1 == s6 (true),這兩個(gè)相等完全歸功于intern()方法芍躏,s5在堆中邪乍,內(nèi)容為"Hello" ,intern方法會(huì)嘗試將"Hello"字符串添加到常量池中对竣,并返回其在常量池中的地址庇楞,因?yàn)槌A砍刂幸呀?jīng)有了"Hello"字符串,所以intern方法直接返回地址否纬;而s1在編譯期就已經(jīng)指向常量池了吕晌,因此s1和s6指向同一地址,相等临燃。
  • 以上所講僅涉及字符串常量池睛驳,實(shí)際上還有整型常量池、浮點(diǎn)型常量池等等膜廊,但都大同小異
  • 數(shù)值類型的常量池不可以手動(dòng)添加常量乏沸,程序啟動(dòng)時(shí)常量池中的常量就已經(jīng)確定了,比如整型常量池中的常量范圍:-128~127爪瓜,只有這個(gè)范圍的數(shù)字可以用到常量池屎蜓。

三個(gè)非常重要的結(jié)論:

  1. 必須要關(guān)注編譯期的行為,才能更好的理解常量池钥勋。
  2. 運(yùn)行時(shí)常量池中的常量炬转,基本來(lái)源于各個(gè)class文件中的常量池辆苔。
  3. 程序運(yùn)行時(shí),除非手動(dòng)向常量池中添加常量(比如調(diào)用intern方法)扼劈,否則jvm不會(huì)自動(dòng)添加常量到常量池驻啤。

三、常量池溢出

/**
 * jdk1.6 -XX:MaxPermSize=5M OutOfMemoryError: PermGen space
 * jdk1.7 -Xmx5M OutOfMemoryError: Java heap space
 * jdk1.8 -Xmx5M OutOfMemoryError: GC overhead limit exceeded
 * jdk1.8 -XX:MaxMetaspaceSize=2M OutOfMemoryError: Metaspace
 */
public class ConstantPoolOOM {

  public static void main(String[] args) throws InterruptedException {
    List<String> list = new ArrayList<String>();
    int index = 0;
    while (true) {
      list.add(String.valueOf(index++).intern());
    }
  }
}
  • jdk1.6運(yùn)行時(shí)常量池在方法區(qū)中荐吵,設(shè)置-XX:MaxPermSize=5M骑冗,導(dǎo)致OutOfMemoryError: PermGen space
  • jdk1.7運(yùn)行時(shí)常量池在堆中,僅僅保存常量的引用先煎,常量對(duì)象在堆中贼涩,設(shè)置-Xmx5M,導(dǎo)致OutOfMemoryError: Java heap space
  • jdk1.8運(yùn)行時(shí)常量池在元空間中薯蝎,僅僅保存常量的引用遥倦,常量對(duì)象在堆中,
    設(shè)置-XX:MaxMetaspaceSize=2M占锯,導(dǎo)致OutOfMemoryError: Metaspace
    設(shè)置-Xmx5M袒哥,導(dǎo)致OutOfMemoryError: GC overhead limit exceeded

四、jdk1.6和1.7+常量池的差異

/**
 * jdk1.6 false false
 * jdk1.7+ true false
 */
public class ConstantPoolTest {

  public static void main(String[] args) {
    String str1=new StringBuilder("計(jì)算機(jī)").append("軟件").toString();
    System.out.println(str1.intern()==str1);
    String str2=new StringBuilder("ja").append("va").toString();
    System.out.println(str2.intern()==str2);
  }
}
  1. 上面代碼在jdk1.6中執(zhí)行會(huì)打印兩個(gè)false
    1.6中intern()方法會(huì)把首次出現(xiàn)的字符串實(shí)例復(fù)制到運(yùn)行時(shí)常量池(方法區(qū))中消略,并返回方法區(qū)中這個(gè)實(shí)例的地址堡称,而str1 str2的地址都在堆中,所以兩次都打印false艺演。
  2. 在jdk1.7種執(zhí)行第一個(gè)會(huì)打印true却紧,第二個(gè)會(huì)打印false
    1.7以后版本intern()方法會(huì)把首次出現(xiàn)的字符串實(shí)例的引用運(yùn)行時(shí)常量池中,
    "計(jì)算機(jī)軟件"是首次出現(xiàn)的字符串胎撤,會(huì)把str1的引用存入運(yùn)行時(shí)常量池啄寡,所以str1.intern()返回的就是str1的引用,打印true哩照。
    "java"在創(chuàng)建str2對(duì)象之前已出現(xiàn)過(guò)挺物,運(yùn)行時(shí)常量池中已經(jīng)存在,所以打印false飘弧。

參考資料:
http://www.cnblogs.com/iyangyuan/p/4631696.html
https://segmentfault.com/a/1190000010412582

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末识藤,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子次伶,更是在濱河造成了極大的恐慌痴昧,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件冠王,死亡現(xiàn)場(chǎng)離奇詭異赶撰,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門豪娜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)餐胀,“玉大人,你說(shuō)我怎么就攤上這事瘤载》裨郑” “怎么了?”我有些...
    開封第一講書人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵鸣奔,是天一觀的道長(zhǎng)墨技。 經(jīng)常有香客問(wèn)我,道長(zhǎng)挎狸,這世上最難降的妖魔是什么扣汪? 我笑而不...
    開封第一講書人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮锨匆,結(jié)果婚禮上崭别,老公的妹妹穿的比我還像新娘。我一直安慰自己统刮,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開白布账千。 她就那樣靜靜地躺著侥蒙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪匀奏。 梳的紋絲不亂的頭發(fā)上鞭衩,一...
    開封第一講書人閱讀 50,096評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音娃善,去河邊找鬼论衍。 笑死,一個(gè)胖子當(dāng)著我的面吹牛聚磺,可吹牛的內(nèi)容都是我干的坯台。 我是一名探鬼主播,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼瘫寝,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蜒蕾!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起焕阿,我...
    開封第一講書人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤咪啡,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后暮屡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體撤摸,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了准夷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钥飞。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖冕象,靈堂內(nèi)的尸體忽然破棺而出代承,到底是詐尸還是另有隱情,我是刑警寧澤渐扮,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布论悴,位于F島的核電站,受9級(jí)特大地震影響墓律,放射性物質(zhì)發(fā)生泄漏膀估。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一耻讽、第九天 我趴在偏房一處隱蔽的房頂上張望察纯。 院中可真熱鬧,春花似錦针肥、人聲如沸饼记。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)具则。三九已至,卻和暖如春具帮,著一層夾襖步出監(jiān)牢的瞬間博肋,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工蜂厅, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留匪凡,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓掘猿,卻偏偏與公主長(zhǎng)得像病游,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子稠通,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351