Java 內(nèi)存區(qū)域和GC機制

一 前言

對于從事C、C++程序開發(fā)的開發(fā)人員來說揪荣,在內(nèi)存管理領(lǐng)域,他們既是擁有最高權(quán)力的“皇帝”又是從事最基礎(chǔ)工作的“勞動人民”——既擁有每一個對象的“所有權(quán)”,又擔(dān)負著每一個對象生命開始到終結(jié)的維護責(zé)任铅檩。

對于Java程序員來說,在虛擬機自動內(nèi)存管理機制的幫助下莽鸿,不再需要為每一個new操作去寫配對的delete/free代碼昧旨,不容易出現(xiàn)內(nèi)存泄漏和內(nèi)存溢出問題,由虛擬機管理內(nèi)存這一切看起來都很美好祥得。不過兔沃,也正是因為Java程序員把內(nèi)存控制的權(quán)力交給了Java虛擬機,一旦出現(xiàn)內(nèi)存泄漏和溢出方面的問題级及,如果不了解虛擬機是怎樣使用內(nèi)存的乒疏,那么排查錯誤將會成為一項異常艱難的工作。

本篇將從概念上介紹Java虛擬機內(nèi)存的各個區(qū)域饮焦,講解這些區(qū)域的作用怕吴、服務(wù)對象以及其中可能產(chǎn)生的問題窍侧,然后介紹Java中GC機制。

二 運行時數(shù)據(jù)區(qū)域

Java虛擬機在執(zhí)行Java程序的過程中會把它所管理的內(nèi)存劃分為若干個不同的數(shù)據(jù)區(qū)域转绷。這些區(qū)域都有各自的用途伟件,以及創(chuàng)建和銷毀的時間,有的區(qū)域隨著虛擬機進程的啟動而存在议经,有些區(qū)域則依賴用戶線程的啟動和結(jié)束而建立和銷毀斧账。Java虛擬機所管理的內(nèi)存將會包括以下幾個運行時數(shù)據(jù)區(qū)域,如圖所示


1.png

2.1 程序計數(shù)器(Program Counter Register)

  • 是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號指示器煞肾。分支咧织、循環(huán)、跳轉(zhuǎn)扯旷、異常處理拯爽、線程恢復(fù)等基礎(chǔ)功能都需要依賴這個計數(shù)器來完成。該區(qū)域是整個內(nèi)存中較小的一塊钧忽。

  • 當(dāng)前線程私有的毯炮,每個線程都有自己計數(shù)器,從而線程切換后能恢復(fù)到正確的執(zhí)行位置耸黑。

  • 如果線程正在執(zhí)行的是一個Java方法桃煎,這個計數(shù)器記錄的是正在執(zhí)行的虛擬機字節(jié)碼指令的地址;如果正在執(zhí)行的是Native方法大刊,這個計數(shù)器值則為空(Undefined)为迈。

  • 此內(nèi)存區(qū)域是唯一一個在Java虛擬機規(guī)范中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域。

2.2 Java虛擬機棧(JVM Stack)

  • 線程私有缺菌,生命周期與線程相同葫辐;
  • 是Java方法執(zhí)行的內(nèi)存模型,方法在執(zhí)行的時候會創(chuàng)建一個棧幀存儲方法的局部變量表(基本類型伴郁、對象引用)耿战、操作數(shù)棧、動態(tài)鏈接焊傅、方法出口等信息剂陡。
  • 每一個方法從調(diào)用直至執(zhí)行完成的過程,就對應(yīng)著一個棧幀在虛擬機棧中入棧到出棧的過程狐胎;
  • 當(dāng)線程請求的棧深度大于虛擬機所允許的深度鸭栖,則StackOverflowError異常;
  • 如果棧的擴展時無法申請到足夠的內(nèi)存握巢,則OutOfMemoryError異常晕鹊。

2.3 本地方法棧(Native Method Stack)

  • 線程私有,生命周期與線程相同;
  • 本地方法棧作用與虛擬機棧非常相似的捏题,區(qū)別是虛擬機棧為虛擬機執(zhí)行Java方法服務(wù)玻褪,而本地方法棧則為虛擬機使用到的Native方法服務(wù);
  • 也會拋出StackOverflowError和OutOfMemoryError公荧。

2.4 Java堆(Java Heap)

  • 被所有線程共享的一塊內(nèi)存區(qū)域带射,在虛擬機啟動時創(chuàng)建,用于存放對象實例循狰;
  • Java堆是垃圾收集器管理的主要區(qū)域窟社;
  • 可以通過-Xmx和-Xms控制堆的大小绪钥;
  • 如果在堆中沒有內(nèi)存完成實例分配灿里,并且堆也無法再擴展時,將會拋出OutOfMemoryError異常程腹。

Java堆中還可以細分為:新生代和老年代匣吊;再細致一點的有Eden空間、From Survivor空間寸潦、To Survivor空間等色鸳,Java堆中的上述各個區(qū)域的分配、回收等細節(jié)下節(jié)再介紹见转。

2.5 方法區(qū)(Method Area)

  • 線程共享
  • 用于存儲已被虛擬機加載的類信息命雀、常量、靜態(tài)變量斩箫、即時編譯器編譯后的代碼等數(shù)據(jù)吏砂。
  • 這個區(qū)域的內(nèi)存回收目標(biāo)主要針對常量池的回收和對類型的卸載。
  • 方法區(qū)無法滿足內(nèi)存分配需求時乘客,將拋出OutOfMemoryError異常狐血。

2.6 運行時常量池(Runtime Constant Pool)

  • 是方法區(qū)的一部分,用于存放編譯期生成的各種字面量和符號引用易核。
  • 當(dāng)常量池?zé)o法再申請到內(nèi)存時氛雪,則拋出OutOfMemoryError異常。

三 GC機制

上節(jié)介紹了Java內(nèi)存運行時區(qū)域的各個部分耸成,其中程序計數(shù)器、虛擬機棧浴鸿、本地方法棧3個區(qū)域隨線程而生井氢,隨線程而滅;而Java堆和方法區(qū)則不一樣岳链,我們只有在程序處于運行期間時才能知道會創(chuàng)建哪些對象花竞,這部分內(nèi)存的分配和回收都是動態(tài)的,垃圾收集器所關(guān)注的是這部分內(nèi)存。后面討論的GC機制也是針對Java堆和方法區(qū)這一部分內(nèi)存區(qū)域约急。

3.1 內(nèi)存中的哪些對象可以回收零远?

3.1.1 堆內(nèi)存

在堆里面存放著Java世界中幾乎所有的對象實例,垃圾收集器在對堆進行回收前厌蔽,要確定這些對象之中哪些還“存活”著牵辣,哪些已經(jīng)“死去”。
(1)引用計數(shù)算法
就是給每個對象加一個計數(shù)器奴饮,如果有一個地方引用就加1纬向,當(dāng)引用失效就減1;當(dāng)計數(shù)器為0戴卜,則認(rèn)為對象是無用的逾条。這種算法實現(xiàn)簡單,判定效率也很高投剥,但缺點在于很難解決對象之間相互循環(huán)引用的問題师脂。
(2)可達性分析算法
基本思路就是通過一系列的稱為“GC Roots”的對象作為起始點,從這些節(jié)點開始向下搜索江锨,搜索所走過的路徑稱為引用鏈(Reference Chain)吃警,當(dāng)一個對象到GC Roots沒有任何引用鏈相連(用圖論的話來說,就是從GC Roots到這個對象不可達)時泳桦,則證明此對象是不可用的汤徽。


2.png

obj8、obj9灸撰、obj10都沒有到GCRoots對象的引用鏈谒府,即便obj9和obj10之間有引用鏈,但是它們到GC Roots是不可達的浮毯,所以它們將會被判定為是可回收的對象完疫。
在Java語言中,可作為GC Roots的對象包括下面幾種:

  • 虛擬機棧(棧幀中的本地變量表)中引用的對象债蓝。
  • 方法區(qū)中類靜態(tài)屬性引用的對象壳鹤。
  • 方法區(qū)中常量引用的對象。
  • 本地方法棧中JNI(即一般說的Native方法)引用的對象饰迹。

判定對象是否存活都與“引用”有關(guān)芳誓,Java引用分為強引用、軟引用啊鸭、弱引用锹淌、虛引用4種,這4種引用強度依次逐漸減弱赠制。

  • 強引用:類似Object obj=new Object()這類的引用赂摆,只要強引用還存在,垃圾收集器永遠不會回收掉被引用的對象。
  • 軟引用:描述一些還有用但并非必需的對象烟号,在系統(tǒng)將要發(fā)生內(nèi)存溢出異常之前绊谭,將會把這些對象列進回收范圍之中進行第二次回收。如果這次回收還沒有足夠的內(nèi)存汪拥,才會拋出內(nèi)存溢出異常达传。提供了SoftReference類來實現(xiàn)軟引用。
  • 弱引用:也是用來描述非必需對象的喷楣,但是它的強度比軟引用更弱一些趟大,被弱引用關(guān)聯(lián)的對象只能生存到下一次垃圾收集發(fā)生之前。當(dāng)垃圾收集器工作時铣焊,無論當(dāng)前內(nèi)存是否足夠逊朽,都會回收掉只被弱引用關(guān)聯(lián)的對象。提供了WeakReference類來實現(xiàn)弱引用曲伊。
  • 虛引用:也稱為幽靈引用或者幻影引用叽讳,它是最弱的一種引用關(guān)系。一個對象是否有虛引用的存在坟募,完全不會對其生存時間構(gòu)成影響岛蚤,也無法通過虛引用來取得一個對象實例。為一個對象設(shè)置虛引用關(guān)聯(lián)的唯一目的就是能在這個對象被收集器回收時收到一個系統(tǒng)通知懈糯。提供了PhantomReference類來實現(xiàn)虛引用涤妒。
3.1.2 方法區(qū)

主要有兩部分:廢棄的常量和無用的類。
廢棄的常量判斷方法和堆中的對象類似赚哗,只要判斷沒有地方引用就可以回收她紫。相比之下,判斷一個類是否無用屿储,條件就比較苛刻贿讹,需要同事滿足下面3個條件才能算是“無用的類”:

  • 該類的所有實例都已經(jīng)被回收,也就是java堆中不存在該類的任何實例够掠;
  • 加載該類的ClassLoader已經(jīng)被回收民褂;
  • 該類對應(yīng)的java.lang.Class對象沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法疯潭。

虛擬機可以對于滿足上面三個條件的無用類進行回收赊堪,僅僅是可以回收,具體能否回收竖哩,JVM提供了-Xnoclassgc參數(shù)進行控制雹食。

3.2 垃圾收集算法

GC有多種算法,不同的算法實現(xiàn)了不同的垃圾回收器期丰。

3.2.1 標(biāo)記-清除算法

算法分為“標(biāo)記”和“清除”兩個階段:首先標(biāo)記出所有需要回收的對象,在標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對象。
特點:
1.是效率問題钝荡,標(biāo)記和清除兩個過程的效率都不高街立;
2.是空間問題,標(biāo)記清除之后會產(chǎn)生大量不連續(xù)的內(nèi)存碎片埠通,空間碎片太多可能會導(dǎo)致以后在程序運行過程中需要分配較大對象時赎离,無法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)另一次垃圾收集動作。


標(biāo)記-清除示意圖.png
3.2.2 復(fù)制算法

復(fù)制算法將可用內(nèi)存按容量劃分為大小相等的兩塊端辱,每次只使用其中的一塊梁剔。當(dāng)這一塊的內(nèi)存用完了,就將還存活著的對象復(fù)制到另外一塊上面舞蔽,然后再把已使用過的內(nèi)存空間一次清理掉荣病。
特點:1.每次都是對整個半?yún)^(qū)進行內(nèi)存回收,內(nèi)存分配時也就不用考慮內(nèi)存碎片等復(fù)雜情況渗柿,2.只要移動堆頂指針个盆,按順序分配內(nèi)存即可,3.實現(xiàn)簡單朵栖,運行高效颊亮。


復(fù)制算法示意圖.png
3.2.3 標(biāo)記-整理算法

標(biāo)記過程仍然與“標(biāo)記-清除”算法一樣,但后續(xù)步驟不是直接對可回收對象進行清理陨溅,而是讓所有存活的對象都向一端移動终惑,然后直接清理掉端邊界以外的內(nèi)存。
特點:不會產(chǎn)生空間碎片门扇。


標(biāo)記-整理.png
3.2.4 分代收集算法

分代收集算法根據(jù)對象存活周期的不同將內(nèi)存劃為幾塊雹有,一般把java堆分為新生代和老年代,這樣就可以根據(jù)各個年代的特點采用最合適的收集算法悯嗓。在新生代中件舵,每次垃圾回收時都發(fā)現(xiàn)大批對象死去,只有少量存活脯厨,那就選用復(fù)制算法铅祸,付出少量復(fù)制成本就可以完成收集。而老年代中對象存活率較高且沒有空間進行擔(dān)保(后面講新生代的擔(dān)保分配)合武,就必須使用“標(biāo)記-清除”或者“標(biāo)記-整理”算法临梗。

3.3 分代垃圾回收

Java堆中的各代分布.gif
3.3.1 Young(年輕代)復(fù)制算法

年輕代分三個區(qū)。一個Eden區(qū)稼跳,兩個Survivor區(qū)盟庞。大部分對象在Eden區(qū)中生成。當(dāng)Eden區(qū)滿時汤善,還存活的對象將被復(fù)制到Survivor區(qū)(兩個中的一個)什猖,當(dāng)這個Survivor區(qū)滿時票彪,此區(qū)的存活對象將被復(fù)制到另外一個Survivor區(qū),當(dāng)這個Survivor去也滿了的時候不狮,從第一個Survivor區(qū)復(fù)制過來的并且此時還存活的對象降铸,將被復(fù)制“年老區(qū)(Tenured)”。需要注意摇零,Survivor的兩個區(qū)是對稱的推掸,沒先后關(guān)系,所以同一個區(qū)中可能同時存在從Eden復(fù)制過來 對象驻仅,和從前一個Survivor復(fù)制過來的對象谅畅,而復(fù)制到年老區(qū)的只有從第一個Survivor去過來的對象。而且噪服,Survivor區(qū)總有一個是空的毡泻。

3.3.2Tenured(年老代)標(biāo)記清除|標(biāo)記整理

年老代存放從年輕代存活的對象。一般來說年老代存放的都是生命期較長的對象芯咧。

3.3.3 Perm(持久代)很難發(fā)生GC

用于存放靜態(tài)文件牙捉,如今Java類、方法等敬飒。持久代對垃圾回收沒有顯著影響邪铲,但是有些應(yīng)用可能動態(tài)生成或者調(diào)用一些class,例如Hibernate等无拗,在這種時候需要設(shè)置一個比較大的持久代空間來存放這些運行過程中新增的類带到。持久代大小通過-XX:MaxPermSize=進行設(shè)置。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末英染,一起剝皮案震驚了整個濱河市揽惹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌四康,老刑警劉巖搪搏,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異闪金,居然都是意外死亡疯溺,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門哎垦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來囱嫩,“玉大人,你說我怎么就攤上這事漏设∧校” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵郑口,是天一觀的道長鸳碧。 經(jīng)常有香客問我盾鳞,道長,這世上最難降的妖魔是什么杆兵? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任雁仲,我火速辦了婚禮,結(jié)果婚禮上琐脏,老公的妹妹穿的比我還像新娘。我一直安慰自己缸兔,他們只是感情好日裙,可當(dāng)我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著惰蜜,像睡著了一般昂拂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上抛猖,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天格侯,我揣著相機與錄音,去河邊找鬼财著。 笑死联四,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的撑教。 我是一名探鬼主播朝墩,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼伟姐!你這毒婦竟也來了收苏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤愤兵,失蹤者是張志新(化名)和其女友劉穎鹿霸,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體秆乳,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡懦鼠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了矫夷。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片葛闷。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖双藕,靈堂內(nèi)的尸體忽然破棺而出淑趾,到底是詐尸還是另有隱情,我是刑警寧澤忧陪,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布扣泊,位于F島的核電站近范,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏延蟹。R本人自食惡果不足惜评矩,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望阱飘。 院中可真熱鬧斥杜,春花似錦、人聲如沸沥匈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽高帖。三九已至缰儿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間散址,已是汗流浹背乖阵。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留预麸,地道東北人瞪浸。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像师崎,于是被迫代替她去往敵國和親默终。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,713評論 2 354

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

  • Java GC(Garbage Collection犁罩,垃圾收集齐蔽,垃圾回收)機制,是Java與C++/C的主要區(qū)別之...
    胡二囧閱讀 1,161評論 0 20
  • 從三月份找實習(xí)到現(xiàn)在床估,面了一些公司含滴,掛了不少,但最終還是拿到小米丐巫、百度谈况、阿里、京東递胧、新浪碑韵、CVTE、樂視家的研發(fā)崗...
    時芥藍閱讀 42,243評論 11 349
  • 寫在前面本文介紹的Java虛擬機(JVM)的自動內(nèi)存管理機制主要是參照《深入理解Java虛擬機》(第2版)一書中的...
    EakonZhao閱讀 4,243評論 5 48
  • 花有重開日缎脾,人無再少年祝闻。歲月蹉跎,人生轉(zhuǎn)轉(zhuǎn)數(shù)十年遗菠,數(shù)不盡的悲傷離合联喘,看不盡的繁華櫻夢华蜒。有人說:愿得一人心,白首不相...
    da010a397713閱讀 339評論 0 0
  • 那條紅色低胸吊帶裙穿在杉杉身上真是再合適不過了豁遭。 它的設(shè)計很有心機叭喜,穿上它后,肩帶會不自覺地滑落蓖谢,露出白皙的鎖骨和...
    哲思少女閱讀 286評論 0 1