深入理解JVM(1) : Java內(nèi)存區(qū)域劃分

Java與C++之間有一堵由內(nèi)存動態(tài)分配和垃圾收集技術(shù)所圍成的“高墻”爆阶,墻外面的人想進(jìn)去佩研,墻里面的人卻想出來捣域。


Java虛擬機(jī)在執(zhí)行Java程序的過程中會把它管理的內(nèi)存劃分為若干個不同的數(shù)據(jù)區(qū)域挂谍。


java虛擬機(jī)運(yùn)行時數(shù)據(jù)區(qū)

一掏觉、程序計數(shù)器


  1. 程序計數(shù)器可以看做是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號指示器。在JVM的概念模型里榴鼎,字節(jié)碼解釋器工作時就是通過改變這個計數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令伯诬。

  2. 由于JVM的多線程是通過線程輪流切換并分配處理器執(zhí)行時間的方式來實現(xiàn)的,為了在線程切換后能恢復(fù)到正確的執(zhí)行位置巫财,每條線程都需要有一個獨(dú)立的程序計數(shù)器盗似,獨(dú)立存儲,互不影響平项。所以赫舒,程序計數(shù)器是線程私有的內(nèi)存區(qū)域。

  3. 如果線程執(zhí)行的是一個Java方法闽瓢,計數(shù)器記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令的地址接癌;
    如果線程執(zhí)行的是一個Native方法,計數(shù)器的值為空扣讼。

  4. Java虛擬機(jī)規(guī)范中唯一一個沒有規(guī)定任何OutOfMemoryError情況的區(qū)域缺猛。

二、Java虛擬機(jī)棧


  1. Java虛擬機(jī)棧描述的是Java方法執(zhí)行的內(nèi)存模型:每個方法執(zhí)行的同時會創(chuàng)建一個棧幀,棧幀用于存儲局部變量表荔燎、操作數(shù)棧耻姥、動態(tài)鏈接、方法出口等信息有咨。每個方法從調(diào)用直至執(zhí)行完成的過程琐簇,就對應(yīng)著一個棧幀在虛擬機(jī)棧中入棧到出棧的過程。

    棧幀

  2. Java虛擬機(jī)棧是線程私有的座享,它的生命周期與線程相同婉商。

  3. 程序員主要關(guān)注的stack棧內(nèi)存,就是虛擬機(jī)棧中局部變量表部分征讲。
    局部變量表存放了編譯時期可知的各種基本數(shù)據(jù)類型對象引用据某。
    局部變量表所需的內(nèi)存空間在編譯時期完成分配,當(dāng)進(jìn)入一個方法時诗箍,這個方法需要在棧幀中分配多大的局部變量空間是完全確定的,在方法運(yùn)行期間不會改變局部變量表的大小挽唉。

  4. Java虛擬機(jī)規(guī)范對這個區(qū)域規(guī)定了兩種異常情況:

  • 如果線程請求的棧深度大于虛擬機(jī)所允許的深度滤祖,將拋出StackOverflowError 異常;
  • 如果虛擬機(jī)椘孔眩可以動態(tài)擴(kuò)展匠童,如果擴(kuò)展時無法申請到足夠的內(nèi)存,就會拋出OutOfMemoryError異常塑顺;
    (當(dāng)前大部分JVM都可以動態(tài)擴(kuò)展汤求,只不過JVM規(guī)范也允許固定長度的虛擬機(jī)棧)

三、本地方法棧


  1. 本地方法棧與虛擬機(jī)棧所發(fā)揮的作用是非常相似的严拒,它們之間的區(qū)別不過是虛擬機(jī)棧為虛擬機(jī)執(zhí)行Java方法服務(wù)(也就是字節(jié)碼)扬绪,而本地方法棧為虛擬機(jī)使用到的Native方法服務(wù)。

  2. Java虛擬機(jī)規(guī)范對本地方法棧使用的語言裤唠、使用方法與數(shù)據(jù)結(jié)構(gòu)并沒有強(qiáng)制規(guī)定挤牛,因此可以由虛擬機(jī)自由實現(xiàn)。例如:HotSpot虛擬機(jī)直接將本地方法棧和虛擬機(jī)棧合二為一种蘸。

  3. 同虛擬機(jī)棧相同墓赴,Java虛擬機(jī)規(guī)范對這個區(qū)域也規(guī)定了兩種異常情況StackOverflowErrorOutOfMemoryError異常。

四航瞭、Java堆


  1. Java堆是被所有的線程共享的一塊內(nèi)存區(qū)域诫硕,在虛擬機(jī)啟動時創(chuàng)建。
    Java堆的唯一目的就是存放對象實例刊侯,幾乎所有對象實例都在這里分配內(nèi)存章办。

  2. Java堆是垃圾回收器管理的主要區(qū)域,因此也被稱為"GC堆"
    從內(nèi)存回收的角度看纲菌,由于現(xiàn)在收集器基本都采用分代收集算法挠日,所以Java堆可以細(xì)分為:新生代、老生代翰舌;
    從內(nèi)存分配的角度看嚣潜,線程共享的Java堆可能劃分出多個線程私有的分配緩沖區(qū)(TLAB);
    不論如何劃分椅贱,都與存放的內(nèi)容無關(guān)懂算,無論哪個區(qū)域,存儲的仍然是對象實例庇麦。

  3. Java虛擬機(jī)規(guī)范規(guī)定计技,Java堆可以處于物理上不連續(xù)的內(nèi)存空間中,只要邏輯上是連續(xù)的即可山橄,就像我們的磁盤空間一樣垮媒。在實現(xiàn)上,既可以是固定大小的航棱,也可以是可擴(kuò)展的睡雇,不過當(dāng)前主流JVM都是按照可擴(kuò)展來實現(xiàn)的。

  4. Java虛擬機(jī)規(guī)范規(guī)定饮醇,如果在堆上沒有內(nèi)存完成實例分配它抱,并且堆上也無法再擴(kuò)展時,將會拋出OutOfMemoryError異常朴艰。

  5. 內(nèi)存泄露和內(nèi)存溢出
    Java堆內(nèi)存的OOM異常是非常常見的異常情況观蓄,重點(diǎn)是根據(jù)內(nèi)存中的對象是否是必要的,來弄清楚到底是出現(xiàn)了內(nèi)存泄露(Memory Leak)還是內(nèi)存溢出(Memory Overflow).

  • 內(nèi)存泄露:指程序中一些對象不會被GC所回收祠墅,它始終占用內(nèi)存侮穿,即被分配的對象引用鏈可達(dá)但已無用。(可用內(nèi)存減少)
  • 內(nèi)存溢出:程序運(yùn)行過程中無法申請到足夠的內(nèi)存而導(dǎo)致的一種錯誤饵隙。內(nèi)存溢出通常發(fā)生于OLD段或Perm段垃圾回收后撮珠,仍然無內(nèi)存空間容納新的Java對象的情況。
    內(nèi)存泄露是內(nèi)存溢出的一種誘因金矛,不是唯一因素芯急。

五、方法區(qū)


  1. 方法區(qū)也是被所有的線程共享的一塊內(nèi)存區(qū)域驶俊。它用于存儲已被虛擬機(jī)加載的類信息娶耍、常量、靜態(tài)變量饼酿、即時編譯器編譯后的代碼等數(shù)據(jù)榕酒。

  2. Java虛擬機(jī)規(guī)范對方法區(qū)的限制非常寬松胚膊,除了和Java堆一樣 不需要連續(xù)的內(nèi)存和可以選擇固定大小或者可擴(kuò)展之外,還可以選擇不實現(xiàn)垃圾回收想鹰。
    這區(qū)域的內(nèi)存回收目標(biāo)主要是針對常量池的回收和類型的卸載紊婉,一般而言,這個區(qū)域的內(nèi)存回收比較難以令人滿意辑舷,尤其是類型的回收喻犁,條件相當(dāng)苛刻,但是這部分區(qū)域的內(nèi)存回收確實是必要的何缓。

  3. Java虛擬機(jī)規(guī)范規(guī)定肢础,當(dāng)方法區(qū)無法滿足內(nèi)存分配的需求時,將拋出OutOfMemoryError異常碌廓。

  4. 運(yùn)行時常量池
    運(yùn)行時常量池是方法區(qū)的一部分传轰。CLass文件中除了有類的版本、字段谷婆、方法慨蛙、接口等描述信息外,還有一項信息是常量池波材,用于存放編譯期生成的各種字面量和符號引用股淡,這部分內(nèi)容將在類加載后進(jìn)入方法區(qū)的運(yùn)行時常量池中存放。
    運(yùn)行時常量池相對于CLass文件常量池的另外一個重要特征是具備動態(tài)性廷区,Java語言并不要求常量一定只有編譯期才能產(chǎn)生,也就是并非預(yù)置入CLass文件中常量池的內(nèi)容才能進(jìn)入方法區(qū)運(yùn)行時常量池贾铝,運(yùn)行期間也可能將新的常量放入池中隙轻,這種特性被開發(fā)人員利用比較多的就是String類的intern()方法。

  5. String.intern()
    String.intern()是一個Native方法垢揩,它的作用是:如果字符串常量池中已經(jīng)包含了一個等于此String對象的字符串玖绿,則返回代表池中這個字符串的String對象;否則叁巨,將此String對象包含的字符串添加到常量池中斑匪,并且返回此字符串的引用。

 public static void main(String[] args) { 
    String str1 = new StringBuilder("計算機(jī)").append("軟件").toString();
    System.out.println(str1.intern() == str1);
    
    String str2 = new StringBuilder("ja").append("va").toString();
    System.out.println(str2.intern() == str2);
  }

這段代碼在JDK1.6中運(yùn)行锋勺,會得到兩個false蚀瘸,而在JDK1.7中運(yùn)行,會得到一個true和一個false庶橱。原因是:

  • 在JDK1.6中intern()方法會把首次遇到的字符串實例復(fù)制到永久代中贮勃,返回的也是永久代中這個字符串實例的引用,而由StringBuilder創(chuàng)建的字符串實例在Java堆上苏章,所以必然不是一個引用寂嘉。
  • 在JDK1.7中intern()方法不會復(fù)制實例奏瞬,只是在常量池中記錄首次出現(xiàn)的實例引用,因此intern()返回的引用和由StringBuilder創(chuàng)建的字符串實例是同一個泉孩。
  • str2返回false是因為Java這個字符串在執(zhí)行StringBuilder("ja").append("va").toString()之前已經(jīng)出現(xiàn)過硼端,字符串常量池中已經(jīng)有它的引用了,不符合首次出現(xiàn)的原則寓搬,而"計算機(jī)軟件"這個字符串是首次出現(xiàn)的珍昨。

推薦閱讀:《深入理解Java虛擬機(jī)》周志明著


[2015.08.29]

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市订咸,隨后出現(xiàn)的幾起案子曼尊,更是在濱河造成了極大的恐慌,老刑警劉巖脏嚷,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件骆撇,死亡現(xiàn)場離奇詭異,居然都是意外死亡父叙,警方通過查閱死者的電腦和手機(jī)神郊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來趾唱,“玉大人涌乳,你說我怎么就攤上這事√瘃” “怎么了夕晓?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長悠咱。 經(jīng)常有香客問我蒸辆,道長,這世上最難降的妖魔是什么析既? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任躬贡,我火速辦了婚禮,結(jié)果婚禮上眼坏,老公的妹妹穿的比我還像新娘拂玻。我一直安慰自己,他們只是感情好宰译,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布檐蚜。 她就那樣靜靜地躺著,像睡著了一般囤屹。 火紅的嫁衣襯著肌膚如雪熬甚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天肋坚,我揣著相機(jī)與錄音乡括,去河邊找鬼肃廓。 笑死,一個胖子當(dāng)著我的面吹牛诲泌,可吹牛的內(nèi)容都是我干的盲赊。 我是一名探鬼主播,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼敷扫,長吁一口氣:“原來是場噩夢啊……” “哼哀蘑!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起葵第,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤绘迁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后卒密,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體缀台,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年哮奇,在試婚紗的時候發(fā)現(xiàn)自己被綠了膛腐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡鼎俘,死狀恐怖哲身,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情贸伐,我是刑警寧澤勘天,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站捉邢,受9級特大地震影響误辑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜歌逢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望翘狱。 院中可真熱鬧秘案,春花似錦、人聲如沸潦匈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽茬缩。三九已至赤惊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間凰锡,已是汗流浹背未舟。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工圈暗, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人裕膀。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓员串,卻偏偏與公主長得像,于是被迫代替她去往敵國和親昼扛。 傳聞我的和親對象是個殘疾皇子寸齐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評論 2 345

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