JVM擴(kuò)展(2):常量池引發(fā)的一連串思考(上)

  百度百科:
  https://baike.baidu.com/item/%E5%B8%B8%E9%87%8F%E6%B1%A0/3855836?fr=aladdin

  先暫時(shí)認(rèn)為:JDK1.8 的所有類型常量池都存儲(chǔ)在元空間(方法區(qū)) (后續(xù)會(huì)有解釋)
目錄
  1:什么叫常量池
  2:常量結(jié)構(gòu)實(shí)例說明
  3:字符串常量特殊講解
  4:拘留字的實(shí)際使用 與  常量池的作用
  5:有什么類型常量池
  6:JVM在JDK 1.6 1.7 1.8到達(dá)經(jīng)歷了什么 (不糾結(jié))
  7:為什么永久代要替換成元空間 
  8:驗(yàn)證移除是否已經(jīng)永久代:
1:什么叫常量池
  常量池在java用于保存在編譯期已確定的麻捻,已編譯的class文件中的一份數(shù)據(jù)埃唯。
  它包括了關(guān)于類,方法,接口等中的常量,也包括字符串常量,如String s = "java"這種申明方式;
  當(dāng)然也可擴(kuò)充,執(zhí)行器產(chǎn)生的常量也會(huì)放入常量池筋蓖,故認(rèn)為常量池是JVM的一塊特殊的內(nèi)存空間卸耘。

  Java是一種動(dòng)態(tài)鏈接的語言,常量池的作用非常重要粘咖,常量池中
  1:字面值:包含代碼中所定義的各種基本類型(如int蚣抗、long等等)和對(duì)象型(如String及數(shù)組)的常量值
  2:符號(hào)引用 ,以文本形式出現(xiàn)的比如:
        類和接口的全限定名瓮下;
        字段的名稱和描述符翰铡;
        方法的名稱和描述符。

 所以讽坏,與Java語言中的所謂“常量”不同锭魔,class文件中的“常量”內(nèi)容很豐富,這些常量集中在class中的一個(gè)區(qū)域存放路呜,一個(gè)緊接著一個(gè)迷捧,這里就稱為“常量池”。
2:常量結(jié)構(gòu)實(shí)例說明
在Java程序中胀葱,有很多的東西是永恒的漠秋,不會(huì)在運(yùn)行過程中變化。

字面值 與 符號(hào)應(yīng)用  舉例(不需要糾結(jié)是哪一個(gè)):

    比如一個(gè)類的名字:ClassTest
    比如一個(gè)類字段的名字/所屬類型:String name ="HeSuiJIn"
    比如一個(gè)常量抵屿,int age =18
    比如一個(gè)類方法的名字/返回類型/參數(shù)名與所屬類型:setName (String name 
 public class ClassTest {
        private String name ="HeSuiJIn";
        private final int age =18 ;
        public void setName (String name ){...}
    }
而這些在JVM解釋執(zhí)行程序的時(shí)候是非常重要的庆锦。
那么編譯器將源程序編譯成class文件后,會(huì)用一部分字節(jié)分類存儲(chǔ)這些代碼轧葛。而這些字節(jié)我們就稱為常量池搂抒。
事實(shí)上,只有JVM加載class后朝群,在方法區(qū)中為它們開辟了空間才更像一個(gè)“池”
3:字符串常量特殊講解
在Java源代碼中的每一個(gè)字面值字符串燕耿,都會(huì)在編譯成class文件階段,形成標(biāo)志號(hào)為8(CONSTANT_String_info)的常量表 姜胖。 

當(dāng)JVM加載 class文件的時(shí)候誉帅,會(huì)為對(duì)應(yīng)的常量池建立一個(gè)內(nèi)存數(shù)據(jù)結(jié)構(gòu),并存放在方法區(qū)中右莱。
同時(shí)JVM會(huì)自動(dòng)為CONSTANT_String_info常量表中的字符串常量的字面值 在堆中創(chuàng)建新的String對(duì)象(intern字符串對(duì)象 蚜锨,又叫拘留字符串對(duì)象)。

然后把CONSTANT_String_info常量表的入口地址轉(zhuǎn)變成這個(gè)堆中String對(duì)象的直接地址(常量池解析)慢蜓。
4:拘留字的實(shí)際使用 與 常量池的作用
拘留字符串對(duì)象
源代碼中所有相同字面值的字符串常量只可能建立唯一 一個(gè)拘留字符串對(duì)象亚再。 
實(shí)際上JVM是通過一個(gè)記錄了拘留字符串引用的內(nèi)部數(shù)據(jù)結(jié)構(gòu)來維持這一特性的。

在Java程序中晨抡,可以調(diào)用String的intern()方法來使得一個(gè)常規(guī)字符串對(duì)象成為拘留字符串對(duì)象氛悬。

(1)String s=new String("Hello world"); 
    事實(shí)上则剃,在加載完成之后,在運(yùn)行這段指令之前如捅,JVM就已經(jīng)為"Hello world"在堆中創(chuàng)建了一個(gè)拘留字符串

     值得注意的是:如果源程序中還有一個(gè)"Hello world"字符串常量棍现,那么他們都對(duì)應(yīng)了同一個(gè)堆中的拘留字符串。
                    1:局部變量s實(shí)際上存儲(chǔ)的是new出來的堆對(duì)象地址镜遣,
                    2:然后用這個(gè)拘留字符串的值來初始化堆中用new指令創(chuàng)建出來的新的String對(duì)象己肮,

(2)String s="Hello world";:
    這跟(1)中創(chuàng)建指令有很大的不同,此時(shí)局部變量s存儲(chǔ)的是早已創(chuàng)建好的拘留字符串的堆地址悲关。

java常量池技術(shù)  java中的常量池技術(shù)谎僻,是為了方便快捷地創(chuàng)建某些對(duì)象而出現(xiàn)的,
當(dāng)需要一個(gè)對(duì)象時(shí)寓辱,就可以從池中取一個(gè)出來(如果池中沒有則創(chuàng)建一個(gè))艘绍,
則在需要重復(fù)創(chuàng)建相等變量時(shí)節(jié)省了很多時(shí)間。
常量池其實(shí)也就是一個(gè)內(nèi)存空間讶舰,常量池存在于方法區(qū)中
5:有什么類型常量池
  Java中的常量池鞍盗,實(shí)際上分為兩種形態(tài):
  靜態(tài)常量池(類常量池)(類文件常量池)(class constant pool) 
  運(yùn)行時(shí)常量池(runtime constant pool)

 Java語言并不要求常量一定只能在編譯期產(chǎn)生:
 1:運(yùn)行期間也可能產(chǎn)生新的常量,這些常量被放在運(yùn)行時(shí)常量池中跳昼“慵祝  
 2:類加載后,同時(shí)靜態(tài)常量池中的數(shù)據(jù)也會(huì)在運(yùn)行時(shí)常量池中存放鹅颊。
 
 這里所說的運(yùn)行期間也可能產(chǎn)生新的常量包括:
 1:基本類型的包裝類
 2:String(也可以通過String.intern()方法可以強(qiáng)制將String放入運(yùn)行時(shí)常量池中)

字符串常量池(String pool)(這里先了解)(不糾結(jié)) 下節(jié)會(huì)詳細(xì)說明
又稱為全局常量池  存放字符串的指針

  由于隨著JDK版本的變遷  存儲(chǔ)常量池的地方不斷的改變 導(dǎo)致各種概念魚龍混雜

  根據(jù)網(wǎng)上的各種說常量池所存儲(chǔ)的位置可能為 永久代 方法區(qū)  元空間 堆
  所以需要先弄清楚這些的概念
6:JVM在JDK 1.6 1.7 1.8到底經(jīng)歷了什么 (不糾結(jié))
這里僅針對(duì)JDK 1.8 做詳細(xì)講解
千萬不要糾結(jié):既然你不是使用 JDK1.6  JDK1.7 
                  那么現(xiàn)在不要把所有概念都混在一起敷存,這樣更難理解。

注意:
    我們只關(guān)注  常量池 永久代 方法區(qū)  元空間 堆 這幾個(gè)關(guān)鍵詞

JDK1.6 
永久代又稱為方法區(qū)
1:存放Class加載相關(guān)信息 
2:靜態(tài)常量池:存放 符號(hào)引用 與 各種字面量
3:運(yùn)行時(shí)常量池
4:字符串常量池

JDK1.7
永久代又稱為方法區(qū)
存放Class加載相關(guān)信息 

其他信息逐步移除 同時(shí)被轉(zhuǎn)移到堆中
1:靜態(tài)常量池:存放 符號(hào)引用 與 各種字面量
2:運(yùn)行時(shí)常量池
3:字符串常量池

JDK1.8
永久代被完全移除   
元空間出現(xiàn) 同時(shí)元空間又稱為方法區(qū)

元空間
存放Class加載相關(guān)信息 

從JVM的內(nèi)存結(jié)構(gòu)來看:
JDK1.6到JDK1.8的過程 
實(shí)際上就是   永久代的功能逐步被削弱 最終被完成移除的過程
7:為什么永久代要替換成元空間
關(guān)于為什么移除永久代堪伍?
    字符串存在永久代中锚烦,容易出現(xiàn)性能問題和內(nèi)存溢出。        
    類及方法的信息等比較難確定其大小帝雇,因此對(duì)于永久代的大小指定比較困難涮俄,
    太小容易出現(xiàn)永久代溢出,太大則容易導(dǎo)致老年代溢出尸闸。        
    永久代會(huì)為 GC 帶來不必要的復(fù)雜度彻亲,并且回收效率偏低。

 元空間的本質(zhì)和永久代類似吮廉,都是對(duì)JVM規(guī)范中方法區(qū)的實(shí)現(xiàn)苞尝。  
 不過元空間與永久代之間最大的區(qū)別在于:元空間并不在虛擬機(jī)中,而是使用本地內(nèi)存宦芦。
 因此宙址,默認(rèn)情況下,元空間的大小僅受本地內(nèi)存限制调卑,但可以通過以下參數(shù)來指定元空間的大新丈啊:
 -XX:MetaspaceSize大咱,初始空間大小
 -XX:MaxMetaspaceSize 最大空間
 
 也因此有時(shí)候我們把元空間 也叫做 方法區(qū)
8:驗(yàn)證移除是否已經(jīng)永久代
    我們可以通過一段程序來比較 JDK 1.6 與 JDK 1.7及 JDK 1.8 的區(qū)別,以字符串常量為例:
public class StringOomMock {
    static String  base = "string";
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        for (int i=0;i< Integer.MAX_VALUE;i++){
            String str = base + base;
            base = str;
            list.add(str.intern());
        }
    }
}
從上述結(jié)果可以看出注益,
JDK 1.6下徽级,會(huì)出現(xiàn)“PermGen Space”的內(nèi)存溢出,
而在 JDK 1.7和 JDK 1.8 中聊浅,會(huì)出現(xiàn)堆內(nèi)存溢出,
并且 JDK 1.8中 PermSize 和 MaxPermGen 已經(jīng)無效现使。
因此低匙,可以大致驗(yàn)證 JDK 1.7 和 1.8 將字符串常量由永久代轉(zhuǎn)移到堆中
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市碳锈,隨后出現(xiàn)的幾起案子顽冶,更是在濱河造成了極大的恐慌,老刑警劉巖售碳,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件强重,死亡現(xiàn)場離奇詭異,居然都是意外死亡贸人,警方通過查閱死者的電腦和手機(jī)间景,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來艺智,“玉大人倘要,你說我怎么就攤上這事∈穑” “怎么了封拧?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長夭问。 經(jīng)常有香客問我泽西,道長,這世上最難降的妖魔是什么缰趋? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任捧杉,我火速辦了婚禮,結(jié)果婚禮上埠胖,老公的妹妹穿的比我還像新娘糠溜。我一直安慰自己,他們只是感情好直撤,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布非竿。 她就那樣靜靜地躺著,像睡著了一般谋竖。 火紅的嫁衣襯著肌膚如雪红柱。 梳的紋絲不亂的頭發(fā)上承匣,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音锤悄,去河邊找鬼韧骗。 笑死,一個(gè)胖子當(dāng)著我的面吹牛零聚,可吹牛的內(nèi)容都是我干的袍暴。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼隶症,長吁一口氣:“原來是場噩夢啊……” “哼政模!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蚂会,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤淋样,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后胁住,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體趁猴,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年彪见,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了儡司。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡企巢,死狀恐怖枫慷,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情浪规,我是刑警寧澤或听,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站笋婿,受9級(jí)特大地震影響誉裆,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜缸濒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一足丢、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧庇配,春花似錦斩跌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春袖订,著一層夾襖步出監(jiān)牢的瞬間氮帐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來泰國打工洛姑, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留上沐,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓楞艾,卻偏偏與公主長得像参咙,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子硫眯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容