Java垃圾收集器與內(nèi)存分配策略

Java垃圾收集器與內(nèi)存分配策略

image.png
  • 程序計(jì)數(shù)器,虛擬機(jī)棧,本地方法棧3個(gè)區(qū)域的內(nèi)存隨線程而生,隨線程而滅,每一個(gè)棧楨分配多少內(nèi)存在類結(jié)構(gòu)確定下來時(shí)就已知的,因此具備確定性,不需要過多考慮回收的問題,方法/線程結(jié)束時(shí)內(nèi)存自然就跟著回收了
  • Java堆,方法區(qū)一個(gè)接口多個(gè)實(shí)現(xiàn)類需要的內(nèi)存不一樣,一個(gè)方法的多個(gè)分支需要的內(nèi)存也不一樣.運(yùn)行時(shí)才知道會(huì)創(chuàng)建哪些對(duì)象,這部分內(nèi)存的分配和回收也是動(dòng)態(tài)的颖变。垃圾收集器所關(guān)注的是這部分內(nèi)存
image.png

對(duì)象已死嗎

<font color=red>垃圾收集器在對(duì)堆回收前,要確定哪些對(duì)象還存活,哪些對(duì)象已死去</font>

引用計(jì)數(shù)法

  • 引用計(jì)數(shù)法
  • 缺點(diǎn): 很難解決對(duì)象之間相互循環(huán)引用的問題,所以主流JVM沒有選用該方法管理內(nèi)存

可達(dá)性分析算法

  • 基本思想: 通過一系列"GC Roots"的對(duì)象作為起點(diǎn),開始向下搜索,走過的路徑稱為引用鏈(Reference Chain),當(dāng)一個(gè)對(duì)象不可達(dá)時(shí),則證明該對(duì)象不可用
  • 可達(dá)性分析算法
  • 可作為GC Roots的對(duì)象包括
    • 虛擬機(jī)棧(棧幀中的本地變量表)中引用的對(duì)象
    • 本地方法棧中Native方法引用的對(duì)象
    • 方法區(qū)中類靜態(tài)屬性引用的對(duì)象
    • 方法區(qū)中常量引用的對(duì)象
  • 對(duì)象的finallize()方法只會(huì)被系統(tǒng)自動(dòng)調(diào)用一次,下次回收時(shí)不會(huì)調(diào)用了鸵钝。該方法由JVM自動(dòng)建立的,低優(yōu)先級(jí)Finalizer線程執(zhí)行,但并不承諾會(huì)等待它運(yùn)行結(jié)束,以防止finallize存在死循環(huán)導(dǎo)致F-Queue隊(duì)列中的其它對(duì)象永久等待.
  • finallize()運(yùn)行代價(jià)高昂,不確定性大,不鼓勵(lì)使用
  • 枚舉根節(jié)點(diǎn)(GC Roots)
    • 問題點(diǎn): 消耗很多時(shí)間
      • 很多應(yīng)用方法區(qū)就有數(shù)百M(fèi),逐個(gè)檢查耗時(shí)
      • GC停頓: GC時(shí)必需停頓所有Java執(zhí)行線程(Stop the World), 防止分析過程中對(duì)象引用關(guān)系還在不斷變化
    • 解決方案: 主流JVM(如HotSpot)使用準(zhǔn)確式GC,HotSpot使用一組OopMap的數(shù)據(jù)結(jié)構(gòu).
      • 類加載完成時(shí): 把對(duì)象內(nèi)什么偏移量上是什么類型的數(shù)據(jù)計(jì)算出來
      • JIT編譯時(shí): 在<font color=red>特定的位置(安全點(diǎn))</font>記錄下棧和寄存器中哪些位置是引用
      • 如果為每條指令都生成OopMap,GC的空間成本會(huì)變得很高,另外OopMap可能會(huì)導(dǎo)致引用關(guān)系變化,而GC時(shí)需要引用關(guān)系不能變化 解決方案是在<font color=red>特定的位置設(shè)置安全點(diǎn),和安全區(qū)域</font>,只有在安全點(diǎn)才能GC停頓.
      • 安全點(diǎn)的選定標(biāo)準(zhǔn):是否具有讓程序長(zhǎng)時(shí)間執(zhí)行的特征.即指令序列復(fù)用,例如:方法調(diào)用,循環(huán)跳轉(zhuǎn),異常跳轉(zhuǎn)等
      • GC時(shí)如何讓所有線程都跑到最近的安全點(diǎn)上再停頓下來?
        • 方案一: 搶占式中斷(已淘汰),GC時(shí)先把所有線程全部中斷, 如發(fā)現(xiàn)有線程中斷的地方不在安全點(diǎn)上,就恢復(fù)線程,讓它跑在安全點(diǎn)上
        • 方案二:主動(dòng)式中斷, GC需要中斷線程時(shí),不直接對(duì)線程操作,僅僅簡(jiǎn)單設(shè)置一個(gè)標(biāo)志,各個(gè)線程執(zhí)行時(shí)主動(dòng)去輪詢(安全點(diǎn)+創(chuàng)建對(duì)象需要分配內(nèi)存的地方進(jìn)行輪詢)這個(gè)標(biāo)志,發(fā)現(xiàn)中斷標(biāo)志為true時(shí),自己就中斷掛起偶惠。
          • 主動(dòng)式中斷
      • 如果程序不執(zhí)行(Sleep,Blocked),則無法進(jìn)入安全點(diǎn)進(jìn)行GC停頓,這時(shí)候需要安全區(qū)域來解決. 安全區(qū)域是指在一段代碼片段中,引用關(guān)系不會(huì)發(fā)生變化.
        • 線程執(zhí)行到安全區(qū)域中的代碼時(shí), 標(biāo)記自己已進(jìn)入了Safe Region.
        • JVM GC時(shí)不管標(biāo)記自己為Safe Region的線程
        • 線程離開Safe Region時(shí),檢查系統(tǒng)是否已完成GC,如果沒有,則等待直到收到可以安全離開Safe Region的信號(hào)為止
          • 安全區(qū)域

引用

引用 說明 舉例
強(qiáng)引用 垃圾收集器<font color=red>永遠(yuǎn)不會(huì)回收</font>強(qiáng)引用的對(duì)象 Object obj = new Object()
軟引用 <font color=red>OutOfMemory異常前</font>回收
弱引用 <font color=red>下次垃圾收集發(fā)生前</font>回收
虛引用 唯一目的是對(duì)象被收集器回收時(shí)收到一個(gè)系統(tǒng)通知.
不會(huì)對(duì)對(duì)象生存時(shí)間構(gòu)成影響, 也無法通過虛引用獲得對(duì)象的實(shí)例
-

垃圾收集算法

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

  • 兩個(gè)階段
    • 標(biāo)記: 先按可達(dá)性算法標(biāo)記出所有需要回收的對(duì)象
    • 回收: 標(biāo)記完成后統(tǒng)一回收,直接對(duì)可回收對(duì)象進(jìn)行清理
  • 缺點(diǎn)
    • 標(biāo)記回收兩個(gè)過程效率都不高
    • 會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片
  • 標(biāo)記-清除算法

標(biāo)記-整理算法

  • Mark-Compact
  • 用于<font color=red>回收老年代</font>
- 標(biāo)記過程 回收過程
標(biāo)記-清除算法 先按可達(dá)性算法標(biāo)記出所有需要回收的對(duì)象 直接對(duì)可回收對(duì)象進(jìn)行清理
標(biāo)記-整理算法 同上 讓所有存活的對(duì)象都向一端移動(dòng),然后直接清理掉端邊界以外的內(nèi)存
  • 標(biāo)記整理算法

復(fù)制算法

  • 將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中一塊.當(dāng)一塊內(nèi)存用完,將存活對(duì)象復(fù)制到另一塊上,然后再把已使用過的內(nèi)存一次清理掉.
  • 復(fù)制算法
  • 解決了效率問題
  • 用于<font color=red>回收新生代</font>
  • 代價(jià)是內(nèi)存縮小了一半
  • IBM研究表明新生代中98%的對(duì)象是朝生夕死的,所以不需要按1:1比例劃分內(nèi)存空間,而是將內(nèi)存劃分為一塊較大的Eden空間和2塊較小的Survivor空間,每次使用Eden和Survior-1.回收時(shí)將Eden和Survior-1復(fù)制到Survior-2,如果Survior-2空間不夠,則使用擔(dān)保內(nèi)存(要還的).

分代收集算法

分代收集算法

垃圾收集器

  • HotSpot JVM的垃圾收集器
  • 發(fā)展歷程: Serial->Parallel->CMS->G1 注意:沒有萬能的收集器,只有最合適的

Serial 收集器

  • 新生代收集器
  • 單線程:進(jìn)行垃圾收集時(shí),必需暫停其它所有的工作線程(Stop The World). 即便是更加先進(jìn)的收集器,也只能不斷縮短用戶線程停頓時(shí)間,而不能完全消除
  • 優(yōu)點(diǎn): 簡(jiǎn)單高效,沒有線程交互的干擾,專心做垃圾手機(jī)
  • 適合運(yùn)行Client模式下的虛擬機(jī)(桌面應(yīng)用場(chǎng)景下分給JVM管理的內(nèi)存不大,停頓時(shí)間幾十ms-100多ms腕柜,可以接受)
  • Serial+Serial Old收集器

ParNew 收集器

  • 新生代收集器
  • Serial收集器的多線程版本
  • Server模式下JVM的首選新生代收集器(除了Serial外,只有它能與CMS配合工作)
  • 多CPU下效果較好,1-2個(gè)CPU效果不一定比Serial好
  • ParNew+Serial Old收集器

Parallel Scavenge 收集器

  • 與ParNew一樣, 特點(diǎn)是它的關(guān)注點(diǎn)與其他收集器不同.
收集器 關(guān)注點(diǎn) 適合
CMS等其他收集器 盡可能縮短垃圾收集時(shí)用戶線程的停頓時(shí)間
Parallel Scavenge 達(dá)到可控制的吞吐量 停頓時(shí)間越短,越適合用戶交互程序|高吞吐量可高效利用CPU時(shí)間,盡快完成程序運(yùn)算任務(wù),適合后臺(tái)運(yùn)算而不需要太多交互的任務(wù)
  • 吞吐量=運(yùn)行用戶代碼時(shí)間/(運(yùn)行用戶代碼時(shí)間+垃圾收集時(shí)間) JVM總共運(yùn)行100min,垃圾收集1min,吞吐量=99%
  • GC自適應(yīng)調(diào)節(jié)策略:JVM根據(jù)系統(tǒng)運(yùn)行情況收集性能監(jiān)控信息,動(dòng)態(tài)調(diào)整參數(shù)以提供合適的停頓時(shí)間或最大的吞吐量, 是與ParNew的一個(gè)重要區(qū)別

Serial Old 收集器

  • Serial 的老年代版本

Parallel Old 收集器

  • Parallel的老年代版本
  • Parallel Scavenge+Parallel Old
收集器 線程 年代 關(guān)注點(diǎn)
Serial 單線程 新生代 關(guān)注用戶線程的停頓時(shí)間
Serial Old 單線程 老年代 關(guān)注用戶線程的停頓時(shí)間
ParNew 多線程 新生代 關(guān)注用戶線程的停頓時(shí)間
Parallel Scavenge 多線程 新生代 <font color=red>關(guān)注吞吐量</font>
Parallel Old 多線程 老年代 <font color=red>關(guān)注吞吐量</font>
CMS 多線程 老年代 關(guān)注用戶線程的停頓時(shí)間

CMS收集器

  • Concurrent Mark Sweep
  • 多線程
  • 老年代收集器
  • 以獲取最短回收停頓時(shí)間為目標(biāo)的收集器
  • 適用于互聯(lián)網(wǎng)站或B/S系統(tǒng)的服務(wù)端
  • 基于標(biāo)記-清除,但更復(fù)雜,有4個(gè)步驟

|階段序號(hào)|階段|描述|速度|Stop The World|(非并行)|
|----|----|----|----|
|1|初始標(biāo)記|僅僅只是標(biāo)記一下能直接關(guān)聯(lián)到對(duì)象的GC Roots|很快|是|否|
|2|并發(fā)標(biāo)記|GC Roots Tracing的過程,標(biāo)記GC鏈中的對(duì)象|很快|否|是|
|3|重新標(biāo)記|修正并發(fā)標(biāo)記期間因用戶程序繼續(xù)運(yùn)作而導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)的那部分對(duì)象的標(biāo)記記錄|初始標(biāo)記<重新標(biāo)記<并發(fā)標(biāo)記|是|是|
|4|并發(fā)清除||耗時(shí)較長(zhǎng)|否|是|

  • CMS收集器
  • 缺點(diǎn)

    • 對(duì)CPU資源非常敏感,并發(fā)階段占用線程(CPU資源)而導(dǎo)致應(yīng)用程序變慢,總吞吐量降低
    • 無法處理浮動(dòng)垃圾:并發(fā)清理階段用戶線程還在運(yùn)行,伴隨新垃圾產(chǎn)生,這部分垃圾在標(biāo)記過程之后,無法當(dāng)次集中處理,只好下次再處理
    • "標(biāo)記-清除"算法導(dǎo)致了大量空間碎片

G1收集器

  • Garbage-First

  • 收集器技術(shù)發(fā)展的最前沿成果之一告丢,正式商用Since JDK1.7

  • 特點(diǎn)

    • 并行與并發(fā): 能充分利用多CPU硬件優(yōu)勢(shì),其他收集器需要Stop The World的地方, CMS依然可以并發(fā)執(zhí)行
    • 分代收集: 即可以收集新生代也可以收集老年代.無須和其他收集器配合
    • 空間整合:整體基于"標(biāo)記-整理"算法,局部基于"整理算法",不會(huì)產(chǎn)生內(nèi)存碎片
    • 可預(yù)測(cè)停頓: 能讓使用者明確在長(zhǎng)度為M ms的時(shí)間片段內(nèi),消耗在垃圾收集上的時(shí)間不超過N ms
  • G1將整個(gè)Java堆劃分為多個(gè)大小相等的獨(dú)立區(qū)域(Region),雖然還保留了新生代和老年代的概念,但不再物理隔離了晃洒,它們都是一部分Region的集合

  • 可預(yù)測(cè)停頓是因?yàn)镚1通過跟蹤各Region里垃圾堆積的價(jià)值大小,維護(hù)優(yōu)先級(jí)列表,根據(jù)允許的收集時(shí)間,優(yōu)先回收價(jià)值最大的Region(Grabage-First的由來),從而避免了在整個(gè)Java堆中進(jìn)行全區(qū)域的垃圾收集

  • G1把內(nèi)存"化整為零"的思路實(shí)現(xiàn)并不簡(jiǎn)單,因?yàn)镽egion并不是孤立的,避免全堆掃描的解決思路如下:

    1. 每個(gè)Region有個(gè)Remembered Set與之對(duì)應(yīng)
    2. JVM發(fā)現(xiàn)程序在對(duì)Reference類型的數(shù)據(jù)進(jìn)行寫操作時(shí), 會(huì)產(chǎn)生一個(gè)Write Barrier暫時(shí)中斷寫操作
    3. 檢查Reference引用的對(duì)象是否處于不同Region中
    4. 是則通過CardTable將相關(guān)Reference記錄到被引用對(duì)象所屬Region的Remembered Set中
    5. 當(dāng)內(nèi)存回收時(shí),GC Roots的枚舉范圍中加入Remembered Set即可保證不對(duì)全堆掃描也不會(huì)有遺漏
  • 不計(jì)算Remembered Set的操作, G1 運(yùn)作的大致步驟如下:

階段序號(hào) 階段 描述 速度 Stop The World 并發(fā)執(zhí)行(非并行)
1 初始標(biāo)記 僅僅只是標(biāo)記一下能直接關(guān)聯(lián)到對(duì)象的GC Roots 很快
2 并發(fā)標(biāo)記 GC Roots Tracing的過程,標(biāo)記GC鏈中的對(duì)象 很快
3 最終標(biāo)記 修正并發(fā)標(biāo)記期間因用戶程序繼續(xù)運(yùn)作而導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)的那部分對(duì)象的標(biāo)記記錄 初始標(biāo)記<重新標(biāo)記<并發(fā)標(biāo)記 可并行
4 篩選回收 對(duì)各Region回收價(jià)值和成本進(jìn)行排序,根據(jù)用戶所期望的GC停頓時(shí)間來指定回收計(jì)劃 耗時(shí)用戶可控制
  • G1收集器

內(nèi)存分配與回收策略

  • 分配策略
  • 空間分配擔(dān)保

    空間分配擔(dān)保
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市唱歧,隨后出現(xiàn)的幾起案子宪摧,更是在濱河造成了極大的恐慌,老刑警劉巖颅崩,帶你破解...
    沈念sama閱讀 222,627評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件几于,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡沿后,警方通過查閱死者的電腦和手機(jī)沿彭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來尖滚,“玉大人喉刘,你說我怎么就攤上這事∑崤” “怎么了睦裳?”我有些...
    開封第一講書人閱讀 169,346評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)撼唾。 經(jīng)常有香客問我廉邑,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,097評(píng)論 1 300
  • 正文 為了忘掉前任鬓催,我火速辦了婚禮肺素,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘宇驾。我一直安慰自己倍靡,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,100評(píng)論 6 398
  • 文/花漫 我一把揭開白布课舍。 她就那樣靜靜地躺著塌西,像睡著了一般。 火紅的嫁衣襯著肌膚如雪筝尾。 梳的紋絲不亂的頭發(fā)上捡需,一...
    開封第一講書人閱讀 52,696評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音筹淫,去河邊找鬼站辉。 笑死,一個(gè)胖子當(dāng)著我的面吹牛损姜,可吹牛的內(nèi)容都是我干的饰剥。 我是一名探鬼主播,決...
    沈念sama閱讀 41,165評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼摧阅,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼汰蓉!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起棒卷,我...
    開封第一講書人閱讀 40,108評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤顾孽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后比规,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體若厚,經(jīng)...
    沈念sama閱讀 46,646評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,709評(píng)論 3 342
  • 正文 我和宋清朗相戀三年蜒什,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了盹沈。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,861評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡吃谣,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出做裙,到底是詐尸還是另有隱情岗憋,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布锚贱,位于F島的核電站仔戈,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜监徘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,196評(píng)論 3 336
  • 文/蒙蒙 一晋修、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧凰盔,春花似錦墓卦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至尿庐,卻和暖如春忠怖,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背抄瑟。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工凡泣, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人皮假。 一個(gè)月前我還...
    沈念sama閱讀 49,287評(píng)論 3 379
  • 正文 我出身青樓鞋拟,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親钞翔。 傳聞我的和親對(duì)象是個(gè)殘疾皇子严卖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,860評(píng)論 2 361

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