Java內(nèi)存分配與垃圾回收

垃圾收集算法

一耐量、 標(biāo)記-清除算法(Mark-Sweep)

算法分為“標(biāo)記”和“清除”兩個(gè)階段,首先標(biāo)記出所有需要回收的對(duì)象哺眯,在標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對(duì)象。標(biāo)記判定為可達(dá)性分析算法秘血。
不足之處有兩個(gè):

  • 效率問(wèn)題,標(biāo)記和清除兩個(gè)過(guò)程的效率都不高评甜;
  • 空間問(wèn)題灰粮,標(biāo)記清除之后會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片,空間碎片太對(duì)可能會(huì)導(dǎo)致以后在程序運(yùn)行的過(guò)程中需要分配較大的對(duì)象時(shí)忍坷,無(wú)法找到足夠的連續(xù)內(nèi)存而不得不提前出發(fā)另一次垃圾收集動(dòng)作粘舟。

二、 復(fù)制算法(Copying)

為了解決效率問(wèn)題佩研,出現(xiàn)了復(fù)制算法柑肴,將可用的容量劃分為大小相等的兩塊,每次使用其中一塊旬薯,當(dāng)這塊內(nèi)存用完了晰骑,就將還存貨的對(duì)象復(fù)制到另一塊上面,然后再把已使用的內(nèi)存空間一次性清理掉绊序。硕舆,內(nèi)存分配不用考慮內(nèi)存碎片等復(fù)雜情況,只要移動(dòng)堆頂指針政模,按順序分配內(nèi)存即可岗宣,實(shí)現(xiàn)簡(jiǎn)單,運(yùn)行高效淋样。算法代價(jià)為內(nèi)存縮小到原來(lái)的一半。

三胁住、 標(biāo)記-整理算法(Mark-Compact)

復(fù)制算法在對(duì)象存活率較高時(shí)趁猴,就要進(jìn)行較多的復(fù)制操作,效率會(huì)變低彪见。更關(guān)鍵的是會(huì)浪費(fèi)50%空間儡司。所以老年代一般不能直接選用這種算法挪捕。
根據(jù)老年代的特點(diǎn)智厌,就提出了一種“標(biāo)記-整理”算法易结,整理不是直接對(duì)可回收對(duì)象進(jìn)行清理凹蜂,而是讓所有存活對(duì)象向一端移動(dòng)醉锅,然后直接清理掉端邊界以外的內(nèi)存贮庞。

四边苹、分代收集算法(Generational Collection)

根據(jù)對(duì)象存活周期的不同凉夯,將內(nèi)存劃分為幾塊淮韭。一般是新生代和年老代垢粮。新生代采用復(fù)制算法。老年代使用“標(biāo)記—清理”或者“標(biāo)記-整理”算法靠粪。
新生代中的對(duì)象98%都是“朝生夕死”的蜡吧,所以不需要1:1比例來(lái)劃分內(nèi)存空間毫蚓,而是將內(nèi)存分為一塊較大的Eden空間和兩個(gè)較小的Survivor空間,每次使用Eden和其中一塊Survivor昔善。當(dāng)回收時(shí)元潘,將Eden區(qū)和Survivor還存活著的對(duì)象一次性地賦值到另外一塊Survivor空間上,然后清理到Eden和剛剛用過(guò)的Survivor空間君仆。

HotSpot算法實(shí)現(xiàn)

1柬批、枚舉根節(jié)點(diǎn)

可達(dá)性分析有一個(gè)問(wèn)題就是會(huì)導(dǎo)致GC進(jìn)行時(shí)必須停止Java執(zhí)行線程,因?yàn)槊杜e根節(jié)點(diǎn)的時(shí)候?qū)ο箨P(guān)系不斷變化時(shí)袖订,分析結(jié)果準(zhǔn)確性就不能得到保證氮帐。即時(shí)是CMS收集器,枚舉根節(jié)點(diǎn)也是必須要停頓的洛姑。
在HotStop的實(shí)現(xiàn)中上沐,使用一組稱為OooMap數(shù)據(jù)結(jié)構(gòu)來(lái)達(dá)到這個(gè)目的,在類加載的時(shí)候HotStop就把對(duì)象內(nèi)什么偏移量上是什么類型的數(shù)據(jù)計(jì)算出來(lái)楞艾,在JIT編譯過(guò)程中参咙,也會(huì)在特定的位置記錄下棧和寄存器中哪些位置是引用。這樣GC在掃描的時(shí)候就可以直接得知這些信息了硫眯。

2蕴侧、安全點(diǎn)及安全區(qū)域

在OopMap的協(xié)助下,HotSpot可以快速且準(zhǔn)確的完成GC Roots的枚舉两入,但是HotSpot并沒(méi)有為每條指令都生成OopMap,因?yàn)檫@樣需要的額外空間太多净宵。
而是在特定的位置記錄了這些信息,這些位置稱為安全點(diǎn)(Safepoint)裹纳,即程序執(zhí)行的時(shí)候并非在所有的地方都能停頓下來(lái)择葡,而是在到達(dá)安全段時(shí)才能暫停。
安全點(diǎn)的選定基于“是否具有讓程序長(zhǎng)時(shí)間執(zhí)行的特征”進(jìn)行選定的剃氧。因?yàn)槊織l指令執(zhí)行的時(shí)間都非常短暫敏储,程序不太可能因?yàn)橹噶盍鏖L(zhǎng)度太長(zhǎng)而過(guò)長(zhǎng)時(shí)間運(yùn)行,“長(zhǎng)時(shí)間運(yùn)行”最明顯的特征就是指令序列復(fù)用朋鞍。例如方法調(diào)用已添,循環(huán)跳轉(zhuǎn),異常跳轉(zhuǎn)等滥酥,具有這種功能的指令才會(huì)產(chǎn)生Safepoint更舞。
還有一個(gè)問(wèn)題,如何在GC時(shí)使所有的線程都跑到最近的安全點(diǎn)再停頓下來(lái)恨狈。兩個(gè)方案

  • 搶先式中斷
    不需要線程主動(dòng)配合疏哗,而是先把所有線程都中斷,然后如果不在安全段就恢復(fù)線程,讓它跑到安全點(diǎn)》捣睿現(xiàn)在基本沒(méi)有這種實(shí)現(xiàn)方式贝搁。
  • 主動(dòng)式中斷
    當(dāng)GC需要中斷線程時(shí),不直接對(duì)線程操作芽偏,而是在安全點(diǎn)以及創(chuàng)建對(duì)象需要分配內(nèi)存的地方設(shè)置一個(gè)標(biāo)志雷逆,各個(gè)線程執(zhí)行時(shí)主動(dòng)去輪詢這個(gè)標(biāo)志,發(fā)現(xiàn)中斷標(biāo)志為真時(shí)就自己中斷線程掛起污尉。
    安全點(diǎn)并沒(méi)有解決膀哲,程序不執(zhí)行時(shí)如何跑到安全點(diǎn)的問(wèn)題。因?yàn)檫@種一般是線程處于sleep狀態(tài)或者Blocked狀態(tài)被碗,這個(gè)時(shí)候線程無(wú)法相應(yīng)JVM的中斷請(qǐng)求某宪。
    這個(gè)時(shí)候就需要安全區(qū)域了,安全區(qū)域是指一段代碼片段中锐朴,引用關(guān)系不會(huì)發(fā)生變化兴喂,這個(gè)區(qū)域任意時(shí)刻開(kāi)始GC都是安全的。安全區(qū)域可以看作是安全點(diǎn)的擴(kuò)展焚志。
    當(dāng)線程執(zhí)行到安全區(qū)域的代碼時(shí)衣迷,首先標(biāo)識(shí)自己進(jìn)入安全區(qū)域了,這時(shí)當(dāng)GC時(shí)酱酬,JVM就不用管標(biāo)識(shí)為安全區(qū)域狀態(tài)的線程了壶谒。

垃圾收集器

圖中展示了7種不同分代的收集器:
Serial、ParNew膳沽、Parallel Scavenge汗菜、Serial Old、Parallel Old贵少、CMS呵俏、G1;

而它們所處區(qū)域滔灶,則表明其是屬于新生代收集器還是老年代收集器:

  • 新生代收集器:Serial、ParNew吼肥、Parallel Scavenge录平;
  • 老年代收集器:Serial Old、Parallel Old缀皱、CMS斗这;
  • 整堆收集器:G1;

兩個(gè)收集器間有連線啤斗,表明它們可以搭配使用:
Serial/Serial Old表箭、Serial/CMS、ParNew/Serial Old钮莲、ParNew/CMS免钻、Parallel Scavenge/Serial Old彼水、Parallel Scavenge/Parallel Old、G1极舔;

并發(fā)垃圾收集和并行垃圾收集的區(qū)別

  • 并行(Parallel)
    指多條垃圾收集線程并行工作凤覆,但此時(shí)用戶線程仍然處于等待狀態(tài);
    如ParNew拆魏、Parallel Scavenge盯桦、Parallel Old;
  • 并發(fā)(Concurrent)
    指用戶線程與垃圾收集線程同時(shí)執(zhí)行(但不一定是并行的渤刃,可能會(huì)交替執(zhí)行)拥峦;
    用戶程序在繼續(xù)運(yùn)行,而垃圾收集程序線程運(yùn)行于另一個(gè)CPU上卖子;
    如CMS略号、G1(也有并行)。

Minor GC和Full GC的區(qū)別

  • Minor GC又稱新生代GC揪胃,指發(fā)生在新生代的垃圾收集動(dòng)作璃哟;
    因?yàn)镴ava對(duì)象大多是朝生夕滅,所以Minor GC非常頻繁喊递,一般回收速度也比較快随闪;
  • Full GC又稱Major GC或老年代GC,指發(fā)生在老年代的GC骚勘;
    出現(xiàn)Full GC經(jīng)常會(huì)伴隨至少一次的Minor GC(不是絕對(duì)铐伴,Parallel Sacvenge收集器就可以選擇設(shè)置Major GC策略);
    Major GC速度一般比Minor GC慢10倍以上俏讹;

1当宴、 Serial收集器

Serial收集器是最基本、發(fā)展歷史最悠久的收集器泽疆。

特點(diǎn)
  • 針對(duì)新生代
  • 采用復(fù)制算法
  • 單線程收集
  • 進(jìn)行垃圾回收時(shí)户矢,必須暫停所有工作線程,直到完成(Stop The World)殉疼。


    image.png
優(yōu)點(diǎn)以及應(yīng)用場(chǎng)景
  • 虛擬機(jī)運(yùn)行在client模式下默認(rèn)新生代收集器
  • 簡(jiǎn)單高效(與其他收集器的單線程比)
參數(shù)設(shè)置

“-XX:UseSerialGC”:添加該參數(shù)來(lái)顯式的使用串行垃圾收集器梯浪;

2、ParNew收集器

ParNew收集器是Serial收集器的多線程版本瓢娜。

特點(diǎn)
  • 除了使用多線程進(jìn)行垃圾收集以外挂洛,其余行為和特點(diǎn)和Serial收集器一樣
  • 兩者共用了很多相同的代碼


    image.png
應(yīng)用場(chǎng)景
  • 在server模式下,ParNew是一個(gè)很重要的收集器眠砾,因?yàn)槌薙erial收集器虏劲,目前只有它能夠與CMS收集器配合工作。
  • 在單個(gè)cup的環(huán)境下,ParNew效率比Serial低柒巫,因?yàn)榇嬖诰€程交互開(kāi)銷励堡。
設(shè)置參數(shù)
  • "-XX:+UseConcMarkSweepGC":指定使用CMS后,會(huì)默認(rèn)使用ParNew作為新生代收集器吻育;
  • "-XX:+UseParNewGC":強(qiáng)制指定使用ParNew念秧;
  • "-XX:ParallelGCThreads":指定垃圾收集的線程數(shù)量,ParNew默認(rèn)開(kāi)啟的收集線程與CPU的數(shù)量相同布疼;
為什么除了Serial只有ParNew能與CMS收集器配合
  • CMS作為老年代收集器摊趾,但卻無(wú)法與JDK1.4已經(jīng)存在的新生代收集器Parallel Scavenge配合工作;
  • 因?yàn)镻arallel Scavenge(以及G1)都沒(méi)有使用傳統(tǒng)的GC收集器代碼框架游两,而另外獨(dú)立實(shí)現(xiàn)砾层;而其余幾種收集器則共用了部分的框架代碼;

3贱案、 Parallel Scavenge收集器

Parallel Scavenge收集器是一個(gè)新生代收集器肛炮,也是使用復(fù)制算法以及并行的多線程收集器。它與吞吐量密切相關(guān)宝踪,也稱為吞吐量收集器(ThroughPut Collector)

特點(diǎn)
  • 前面寫的侨糟,與ParNew相似,新生代瘩燥、復(fù)制秕重、多線程收集;
  • 它的關(guān)注點(diǎn)與其他收集器不同厉膀,其他都是盡可能地縮短垃圾收集時(shí)用戶線程的停頓時(shí)間溶耘,而它的目標(biāo)是達(dá)到一個(gè)可以控制的吞吐量(Throughput)。
    吞吐量=運(yùn)行用戶代碼時(shí)間/(運(yùn)行用戶代碼時(shí)間+垃圾回收時(shí)間)
應(yīng)用場(chǎng)景
  • 高吞吐量為目標(biāo)服鹅,可以高效率地利用CUP時(shí)間凳兵,盡快地完成計(jì)算任務(wù),主要適合后臺(tái)運(yùn)行且不需要太多交互的任務(wù)企软。例如庐扫,那些執(zhí)行批量處理、訂單處理仗哨、工資支付聚蝶、科學(xué)計(jì)算的應(yīng)用程序。
設(shè)置參數(shù)
  • "-XX:MaxGCPauseMillis":控制最大垃圾收集停頓時(shí)間藻治,大于0的毫秒數(shù);
  • "-XX:GCTimeRatio": 設(shè)置垃圾收集時(shí)間占總時(shí)間的比率巷挥,相當(dāng)于吞吐量的倒數(shù)桩卵,0<n<100的整數(shù);
    垃圾收集執(zhí)行時(shí)間占應(yīng)用程序執(zhí)行時(shí)間的比例的計(jì)算方法是:
    1 / (1 + n)
    例如,選項(xiàng)-XX:GCTimeRatio=19雏节,設(shè)置了垃圾收集時(shí)間占總時(shí)間的5% -- 1/(1+19)胜嗓;
    默認(rèn)值是1%--1/(1+99),即n=99钩乍;
  • "-XX:+UseAdptiveSizePolicy":采用自適應(yīng)調(diào)節(jié)策略
    開(kāi)啟這個(gè)參數(shù)后辞州,就不用手工指定一些細(xì)節(jié)參數(shù),如:新生代的大辛却狻(-Xmn)变过、Eden與Survivor區(qū)的比例(-XX:SurvivorRation)、晉升老年代的對(duì)象年齡(-XX: PretenureSizeThreshold)等涝涤;
    JVM會(huì)根據(jù)當(dāng)前系統(tǒng)運(yùn)行情況收集性能監(jiān)控信息媚狰,動(dòng)態(tài)調(diào)整這些參數(shù),以提供最合適的停頓時(shí)間或最大的吞吐量阔拳,這種調(diào)節(jié)方式稱為GC自適應(yīng)的調(diào)節(jié)策略(GC Ergonomiscs)
    這是一種值得推薦的方式:
  • 只需設(shè)置好內(nèi)存數(shù)據(jù)大姓腹隆(如"-Xmx"設(shè)置最大堆);
  • 然后使用"-XX:MaxGCPauseMillis"或"-XX:GCTimeRatio"給JVM設(shè)置一個(gè)優(yōu)化目標(biāo)糊肠;
  • 那些具體細(xì)節(jié)參數(shù)的調(diào)節(jié)就由JVM自適應(yīng)完成辨宠;
    這也是Parallel Scavenge收集器與ParNew收集器一個(gè)重要區(qū)別;

四货裹、Serial Old收集器

Serial Old是 Serial收集器的老年代版本嗤形;

特點(diǎn)
  • 針對(duì)老年代;
  • 采用"標(biāo)記-整理"算法(還有壓縮泪酱,Mark-Sweep-Compact)派殷;
  • 單線程收集;
應(yīng)用場(chǎng)景
  • 主要應(yīng)用于Client模式墓阀;
  • 在JDK1.5及之前毡惜,與Parallel Scavenge收集器搭配使用(JDK1.6有Parallel Old收集器可搭配;
  • 作為CMS收集器的后備預(yù)案斯撮,在并發(fā)收集發(fā)生Concurrent Mode Failure時(shí)使用

五经伙、 Parallel Old收集器

Parallel Old垃圾收集器是Parallel Scavenge收集器的老年代版本;JDK1.6中才開(kāi)始提供勿锅;

特點(diǎn)
  • 針對(duì)老年代帕膜;
  • 采用"標(biāo)記-整理"算法;
  • 多線程收集溢十;


應(yīng)用場(chǎng)景
  • JDK1.6及之后用來(lái)代替老年代的Serial Old收集器垮刹;
  • 特別是在Server模式,多CPU的情況下张弛;
  • 這樣在注重吞吐量以及CPU資源敏感的場(chǎng)景荒典,就有了Parallel Scavenge加Parallel Old收集器的"給力"應(yīng)用組合酪劫;
設(shè)置參數(shù)
  • "-XX:+UseParallelOldGC":指定使用Parallel Old收集器;

六寺董、 CMS收集器(Concurrent Mark Sweep)

并發(fā)標(biāo)記清理(Concurrent Mark Sweep覆糟,CMS)收集器也稱為并發(fā)低停頓收集器(Concurrent Low Pause Collector)或低延遲(low-latency)垃圾收集器。

特點(diǎn)
  • 針對(duì)老年代遮咖;
  • 基于標(biāo)記-清除算法(不進(jìn)行壓縮操作滩字,會(huì)產(chǎn)生內(nèi)存碎片);
    -以獲取最短回收停頓時(shí)間為目標(biāo)御吞;
  • 并發(fā)收集麦箍、低停頓
  • 需要更多的內(nèi)存
應(yīng)用場(chǎng)景
  • 與用戶交互場(chǎng)景較多的場(chǎng)景
  • 希望系統(tǒng)停頓世家年最短,注重服務(wù)的響應(yīng)時(shí)間
  • 比如場(chǎng)景的web魄藕,B/S系統(tǒng)的服務(wù)器上的應(yīng)用
設(shè)置參數(shù)

"-XX:+UseConcMarkSweepGC":指定使用CMS收集器内列;


CMS收集器運(yùn)行過(guò)程
  • 1、 初始標(biāo)記(CMS initial mark)
    僅標(biāo)記一下GC Roots能直接關(guān)聯(lián)到的對(duì)象背率,速度很快话瞧,但是會(huì)Stop The World
  • 2 寝姿、并發(fā)標(biāo)記(CMS concurrent mark)
    進(jìn)行GC Roots Tracing的過(guò)程交排,標(biāo)記出剛才產(chǎn)生的集合中活的對(duì)象,應(yīng)用程序在運(yùn)行饵筑,并不能保障可以標(biāo)記所有的存活對(duì)象埃篓。
  • 3、重新標(biāo)記(CMS remark)
    為了修正上一步并發(fā)標(biāo)記期間因用戶程序繼續(xù)運(yùn)行導(dǎo)致標(biāo)記變動(dòng)的那部分對(duì)象的標(biāo)記記錄根资;
    需要Stop The World 停頓時(shí)間比初始標(biāo)記稍長(zhǎng)架专,但遠(yuǎn)比并發(fā)標(biāo)記時(shí)間短;
    采用多線程并行執(zhí)行來(lái)提升效率玄帕。
  • 4部脚、并發(fā)清除(CMS concurrent sweep)
    回收所有垃圾對(duì)象。

整個(gè)過(guò)程中耗時(shí)最長(zhǎng)的并發(fā)標(biāo)記和并發(fā)清除都可以與用戶線程一起工作裤纹;所以總體上說(shuō)委刘,CMS收集器的內(nèi)存回收過(guò)程與用戶線程一起并發(fā)執(zhí)行;

缺點(diǎn)
  • 1鹰椒、對(duì)CPU資源非常敏感
    并發(fā)收集雖然不會(huì)暫停用戶線程锡移,但因?yàn)檎加靡徊糠諧PU資源,還是會(huì)導(dǎo)致應(yīng)用程序變慢漆际,總吞吐量降低淆珊。
    CMS的默認(rèn)收集線程數(shù)量是=(CPU數(shù)量+3)/4;
    當(dāng)CPU數(shù)量多于4個(gè)奸汇,收集線程占用的CPU資源多于25%套蒂,對(duì)用戶程序影響可能較大钞支;不足4個(gè)時(shí),影響更大操刀,可能無(wú)法接受。
  • 2婴洼、無(wú)法處理浮動(dòng)垃圾
    并發(fā)清除時(shí)骨坑,用戶線程新產(chǎn)生的垃圾,稱為浮動(dòng)垃圾柬采;只能留到下次GC清理欢唾,這使得并發(fā)清除時(shí)需要預(yù)留一定的內(nèi)存空間給用戶線程使用,不能像其他收集器在老年代幾乎填滿再進(jìn)行收集粉捻;
    可以認(rèn)為CMS所需要的空間比其他垃圾收集器大礁遣;
    "-XX:CMSInitiatingOccupancyFraction":設(shè)置CMS預(yù)留內(nèi)存空間;
    • JDK1.5默認(rèn)值為68%肩刃;
    • JDK1.6變?yōu)榇蠹s92%祟霍;
  • 可能出現(xiàn)"Concurrent Mode Failure"失敗而導(dǎo)致另一次Full GC 。
    如果CMS預(yù)留內(nèi)存空間無(wú)法滿足程序需要盈包,就會(huì)出現(xiàn)一次"Concurrent Mode Failure"失敗;
    這時(shí)JVM啟用后備預(yù)案:臨時(shí)啟用Serail Old收集器沸呐,而導(dǎo)致另一次Full GC的產(chǎn)生;
    這樣的代價(jià)是很大的呢燥,所以CMSInitiatingOccupancyFraction不能設(shè)置得太大崭添。
  • 3、產(chǎn)生大量?jī)?nèi)存碎片
    由于CMS基于"標(biāo)記-清除"算法叛氨,清除后不進(jìn)行壓縮操作呼渣;
    產(chǎn)生大量不連續(xù)的內(nèi)存碎片會(huì)導(dǎo)致分配大內(nèi)存對(duì)象時(shí),無(wú)法找到足夠的連續(xù)內(nèi)存寞埠,從而需要提前觸發(fā)另一次Full GC動(dòng)作屁置。
    • 解決辦法
    • "-XX:+UseCMSCompactAtFullCollection":默認(rèn)開(kāi)啟
      使得CMS出現(xiàn)上面這種情況時(shí)不進(jìn)行Full GC,而開(kāi)啟內(nèi)存碎片的合并整理過(guò)程畸裳;
      但合并整理過(guò)程無(wú)法并發(fā)缰犁,停頓時(shí)間會(huì)變長(zhǎng);
    • "-XX:+CMSFullGCsBeforeCompaction":設(shè)置執(zhí)行多少次不壓縮的Full GC后怖糊,來(lái)一次壓縮整理帅容;
      為減少合并整理過(guò)程的停頓時(shí)間;
      默認(rèn)為0伍伤,也就是說(shuō)每次都執(zhí)行Full GC并徘,都會(huì)進(jìn)行壓縮整理;
總結(jié)

總體來(lái)看扰魂,與Parallel Old垃圾收集器相比麦乞,CMS減少了執(zhí)行老年代垃圾收集時(shí)應(yīng)用暫停時(shí)間蕴茴;
但卻增加了新生代垃圾收集時(shí)應(yīng)用暫停的時(shí)間、降低了吞吐量而且需要占用更大的堆空間姐直;

七倦淀、G1收集器(Garbage-First)

特點(diǎn)
  • 1、并行與并發(fā)
    • 面向服務(wù)器應(yīng)用的收集器
    • 能充分利用多CPU声畏、多核環(huán)境下的硬件優(yōu)勢(shì)撞叽;
    • 可以并行來(lái)縮短"Stop The World"停頓時(shí)間;
    • 也可以并發(fā)讓垃圾收集與用戶程序同時(shí)進(jìn)行插龄;
  • 2愿棋、分代收集
    能獨(dú)立管理整個(gè)GC堆(新生代和老年代),而不需要與其他收集器搭配均牢;能夠采用不同方式處理不同時(shí)期的對(duì)象糠雨;
    雖然保留分代概念,但Java堆的內(nèi)存布局有很大差別 將整個(gè)堆劃分為多個(gè)大小相等的獨(dú)立區(qū)域(Region)徘跪;新生代和老年代不再是物理隔離甘邀,它們都是一部分Region(不需要連續(xù))的集合;
  • 3真椿、 結(jié)合多種垃圾收集算法鹃答,空間整合,不產(chǎn)生碎片
    從整體看突硝,是基于標(biāo)記-整理算法测摔;從局部(兩個(gè)Region間)看,是基于復(fù)制算法解恰;
    這是一種類似火車算法的實(shí)現(xiàn)锋八;都不會(huì)產(chǎn)生內(nèi)存碎片,有利于長(zhǎng)時(shí)間運(yùn)行护盈;
  • 4挟纱、 可預(yù)測(cè)的停頓:低停頓的同時(shí)實(shí)現(xiàn)高吞吐量
    G1除了追求低停頓處,還能建立可預(yù)測(cè)的停頓時(shí)間模型腐宋;
    可以明確指定M毫秒時(shí)間片內(nèi)紊服,垃圾收集消耗的時(shí)間不超過(guò)N毫秒;
應(yīng)用場(chǎng)景
  • 面向服務(wù)端應(yīng)用胸竞,針對(duì)具有大內(nèi)存欺嗤、多處理器的機(jī)器;
  • 最主要的應(yīng)用是為需要低GC延遲卫枝,并具有大堆的應(yīng)用程序提供解決方案煎饼;
  • 如:在堆大小約6GB或更大時(shí),可預(yù)測(cè)的暫停時(shí)間可以低于0.5秒校赤;
  • 用來(lái)替換掉JDK1.5中的CMS收集器吆玖;
設(shè)置參數(shù)
  • "-XX:+UseG1GC":指定使用G1收集器筒溃;
  • "-XX:InitiatingHeapOccupancyPercent":當(dāng)整個(gè)Java堆的占用率達(dá)到參數(shù)值時(shí),開(kāi)始并發(fā)標(biāo)記階段沾乘;默認(rèn)為45怜奖;
  • "-XX:MaxGCPauseMillis":為G1設(shè)置暫停時(shí)間目標(biāo),默認(rèn)值為200毫秒意鲸;
  • "-XX:G1HeapRegionSize":設(shè)置每個(gè)Region大小烦周,范圍1MB到32MB;目標(biāo)是在最小Java堆時(shí)可以擁有約2048個(gè)Region怎顾;
為什么G1收集器可以實(shí)現(xiàn)可預(yù)測(cè)的停頓?
  • 1、可以有計(jì)劃地避免在java堆上進(jìn)行全區(qū)域的垃圾收集漱贱;
    G1跟蹤各個(gè)Region獲得其收集價(jià)值大谢蔽怼(回收所獲得的空間大小以及回收所需要時(shí)間的經(jīng)驗(yàn)值),在后臺(tái)維護(hù)一個(gè)優(yōu)先列表幅狮;每次根據(jù)允許的收集時(shí)間募强,優(yōu)先回收價(jià)值最大的Region(名稱Garbage-First的由來(lái))。這就保證了在有限的時(shí)間內(nèi)可以獲取盡可能高的收集效率崇摄;
一個(gè)對(duì)象被不同區(qū)域引用的問(wèn)題

判斷對(duì)象存活時(shí)擎值,是否需要掃描整個(gè)Java堆才能保證準(zhǔn)確?在其他的分代收集器逐抑,也存在這樣的問(wèn)題(而G1更突出):新生代回收的時(shí)候不得不掃描老年代鸠儿?
無(wú)論G1還是其他分代收集器,JVM都是使用Remembered Set來(lái)避免全局掃描:
1厕氨、每個(gè)Region都有一個(gè)對(duì)應(yīng)的Remembered Set进每;
2、 每次Reference類型數(shù)據(jù)寫操作時(shí)命斧,都會(huì)產(chǎn)生一個(gè)Write Barrier暫時(shí)中斷操作田晚;
3、然后檢查將要寫入的引用指向的對(duì)象是否和該Reference類型數(shù)據(jù)在不同的 Region(其他收集器:檢查老年代對(duì)象是否引用了新生代對(duì)象)国葬;
4贤徒、如果不同,通過(guò)CardTable把相關(guān)引用信息記錄到引用指向?qū)ο笏赗egion對(duì)應(yīng)的Remembered Set中汇四;
5接奈、進(jìn)行垃圾收集時(shí),在GC Roots的枚舉范圍加入Remembered Set船殉,就可以保證不進(jìn)行全局掃描鲫趁,也不會(huì)有遺漏。

G1收集器運(yùn)行過(guò)程
  • 1利虫、初始標(biāo)記(Initial Marking)
    僅標(biāo)記一下GC Roots能直接關(guān)聯(lián)到的對(duì)象挨厚;并且修改TAMS(Next Top at Mark Start)堡僻,讓下一階段并發(fā)標(biāo)記時(shí),用戶程序能夠在正確可用的Region中創(chuàng)建新的對(duì)象疫剃。需要"Stop The World"钉疫,但速度很快;
  • 2 巢价、并發(fā)標(biāo)記(Concurrent Marking)
    • 進(jìn)行GC Roots Tracing的過(guò)程牲阁;
    • 剛才產(chǎn)生的集合中標(biāo)記出存活對(duì)象;
    • 耗時(shí)較長(zhǎng)壤躲,但應(yīng)用程序也在運(yùn)行城菊;
    • 并不能保證可以標(biāo)記出所有的存活對(duì)象;
  • 3碉克、“最終標(biāo)記”(Final Marking)
    為了修正并發(fā)標(biāo)記期間因用戶程序繼續(xù)運(yùn)作而導(dǎo)致標(biāo)記變動(dòng)的那一部分對(duì)象的標(biāo)記記錄凌唬; 上一階段對(duì)象的變化記錄在線程的Remembered Set Log;這里把Remembered Set Log合并到Remembered Set中漏麦;
    需要"Stop The World"客税,且停頓時(shí)間比初始標(biāo)記稍長(zhǎng),但遠(yuǎn)比并發(fā)標(biāo)記短撕贞;采用多線程并行執(zhí)行來(lái)提升效率更耻;
  • 4、 篩選回收(Live Data Counting and Evacuation)
    • 首先排序各個(gè)Region的回收價(jià)值和成本捏膨;
    • 然后根據(jù)用戶期望的GC停頓時(shí)間來(lái)制定回收計(jì)劃秧均;
    • 最后按計(jì)劃回收一些價(jià)值高的Region中垃圾對(duì)象;
      回收時(shí)采用"復(fù)制"算法脊奋,從一個(gè)或多個(gè)Region復(fù)制存活對(duì)象到堆上的另一個(gè)空的Region熬北,并且在此過(guò)程中壓縮和釋放內(nèi)存;可以并發(fā)進(jìn)行诚隙,降低停頓時(shí)間讶隐,并增加吞吐量;

這里有一個(gè)疑問(wèn) 篩選回收時(shí)會(huì)Stop The World嘛久又?
深入理解JAVA虛擬機(jī)第二版中是會(huì)的巫延。

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

對(duì)于Client模式下的JVM來(lái)說(shuō)(只有32位的JDK安裝才有Client模式的JVM,64位的JDK只有Server模式的JVM)地消,默認(rèn)的新生代和老年代的垃圾收集器是單線程的Serial(復(fù)制算法炉峰,并且存在擔(dān)保機(jī)制,默認(rèn)Eden區(qū)和Survivor是8:1)和Serial Old(標(biāo)記-整理算法)脉执。下面將研究疼阔,在這種組合的情況下,JVM的內(nèi)存分配和回收策略(ParNew和Serial的組合也差不多)。

  • 對(duì)象優(yōu)先分配到新生代的Eden區(qū)
  • 大對(duì)象直接進(jìn)入老年代
    所謂的大對(duì)象就是指需要大量連續(xù)內(nèi)存空間的JAVA對(duì)象婆廊,最典型的大對(duì)象就是那種很長(zhǎng)的字符串和數(shù)組迅细。經(jīng)常產(chǎn)生大對(duì)象容易導(dǎo)致額外的GC操作,JVM中提供了一個(gè)-XX:PretenureSizeThreshold參數(shù)(這個(gè)參數(shù)只對(duì)Serial和ParNew這兩個(gè)新生代垃圾收集器有效)淘邻,令大于這個(gè)參數(shù)的對(duì)象直接在老年代中分配茵典,這樣做的目的是避免在Eden區(qū)和兩個(gè)Survivor區(qū)之間發(fā)生大量的內(nèi)存拷貝。為什么宾舅?就在于Serial使用的是復(fù)制算法统阿。
  • 長(zhǎng)期存活的對(duì)象將進(jìn)入老年代
    JVM產(chǎn)生一個(gè)對(duì)象的時(shí)候,首先將其放在新生代的Eden區(qū)中,并且隨著mirror GC的產(chǎn)生,大部分的對(duì)象都被回收了愉耙,那么“熬過(guò)”這次GC的對(duì)象呢?JVM給了每個(gè)對(duì)象一個(gè)“年齡計(jì)數(shù)器”蜻直,所謂的年齡計(jì)數(shù)器就是指,這個(gè)對(duì)象熬過(guò)第一次GC袁串,并且進(jìn)入了Survivor區(qū)中,那么就將這個(gè)對(duì)象的年齡設(shè)為1呼巷,之后囱修,每熬過(guò)一次GC,年齡+1王悍,當(dāng)這個(gè)值到達(dá)一個(gè)閥值(默認(rèn)15破镰,可通過(guò)-XX:MaxTenuringThreshold來(lái)設(shè)置)時(shí),這個(gè)對(duì)象就會(huì)被移到老年代中压储。
  • 動(dòng)態(tài)對(duì)象年齡判斷
    為了更好適應(yīng)不同程序的內(nèi)存狀況鲜漩,JVM也不是要一個(gè)對(duì)象必須滿足MaxTenuringThreshold設(shè)置的年齡閥值才能進(jìn)入老年代。如果Survivor中的對(duì)象滿足同年齡(比如N)對(duì)象所占空間達(dá)到了Survivor總空間的一半的時(shí)候集惋,那么年齡大于或者等于N的對(duì)象都可以進(jìn)入老年代孕似,無(wú)需等待閥值。
  • 空間分配擔(dān)保
    新生代采用復(fù)制算法刮刑,會(huì)造成空間的浪費(fèi)喉祭,故而提出了一種“空間擔(dān)保機(jī)制”來(lái)提高復(fù)制算法的空間利用率,使復(fù)制算法的浪費(fèi)從50%降到了10%雷绢。而老年代的內(nèi)存就充當(dāng)了這個(gè)擔(dān)保者泛烙,并且由于沒(méi)有其他內(nèi)存來(lái)?yè)?dān)保老年代,所以老年代如果不想產(chǎn)生空間內(nèi)存碎片那么只能使用“標(biāo)記-整理”算法翘紊。
    如何保證老年代有足夠的空間來(lái)執(zhí)行空間擔(dān)保機(jī)制呢蔽氨?Full GC,是否觸發(fā)根據(jù)經(jīng)驗(yàn)值判斷,即使不允許擔(dān)保失敗鹉究,也有可能發(fā)生擔(dān)保失敗宇立。
    當(dāng)發(fā)生YGC的時(shí)候,JVM都會(huì)檢測(cè)之前每次晉升到老年代的對(duì)象的平均大小是否大于老年代的剩余內(nèi)存空間坊饶,如果大于泄伪,則觸發(fā)Full GC;如果小于匿级,則查看HandlePromotionFailure設(shè)置是否允許擔(dān)保失旙巍;如果允許痘绎,則不會(huì)觸發(fā)Full GC津函,反之,觸發(fā)Full GC孤页,保證老年代有足夠的空間支持空間分配擔(dān)保成功尔苦。
    在每次GC發(fā)生的時(shí)候,我們也不知道到底會(huì)有多少對(duì)象被回收行施,又有多少對(duì)象能存活允坚。故而只好取之前每次回收晉升到老年代的對(duì)象的平均值作為經(jīng)驗(yàn)值來(lái)判斷,但是如果某次GC后存活對(duì)象激增蛾号,仍然會(huì)導(dǎo)致?lián)J〕硐睿敲粗荒苤匦逻M(jìn)行Full GC了,雖然這樣會(huì)繞個(gè)圈子鲜结,但是大部分情況下還是會(huì)將HandlePromotionFailure的值設(shè)為true展运,從而避免Full GC過(guò)于頻繁。換句話說(shuō)精刷,就是大部分情況拗胜,允許擔(dān)保失敗。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末怒允,一起剝皮案震驚了整個(gè)濱河市埂软,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌误算,老刑警劉巖仰美,帶你破解...
    沈念sama閱讀 212,222評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異儿礼,居然都是意外死亡咖杂,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,455評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門蚊夫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)诉字,“玉大人,你說(shuō)我怎么就攤上這事∪榔裕” “怎么了陵霉?”我有些...
    開(kāi)封第一講書人閱讀 157,720評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)伍绳。 經(jīng)常有香客問(wèn)我踊挠,道長(zhǎng),這世上最難降的妖魔是什么冲杀? 我笑而不...
    開(kāi)封第一講書人閱讀 56,568評(píng)論 1 284
  • 正文 為了忘掉前任效床,我火速辦了婚禮,結(jié)果婚禮上权谁,老公的妹妹穿的比我還像新娘剩檀。我一直安慰自己,他們只是感情好旺芽,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,696評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布沪猴。 她就那樣靜靜地躺著,像睡著了一般采章。 火紅的嫁衣襯著肌膚如雪运嗜。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,879評(píng)論 1 290
  • 那天悯舟,我揣著相機(jī)與錄音洗出,去河邊找鬼。 笑死图谷,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的阱洪。 我是一名探鬼主播便贵,決...
    沈念sama閱讀 39,028評(píng)論 3 409
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼冗荸!你這毒婦竟也來(lái)了承璃?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 37,773評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蚌本,失蹤者是張志新(化名)和其女友劉穎盔粹,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體程癌,經(jīng)...
    沈念sama閱讀 44,220評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡舷嗡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,550評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了嵌莉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片进萄。...
    茶點(diǎn)故事閱讀 38,697評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出中鼠,到底是詐尸還是另有隱情可婶,我是刑警寧澤,帶...
    沈念sama閱讀 34,360評(píng)論 4 332
  • 正文 年R本政府宣布援雇,位于F島的核電站矛渴,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏惫搏。R本人自食惡果不足惜具温,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,002評(píng)論 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望晶府。 院中可真熱鬧桂躏,春花似錦、人聲如沸川陆。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,782評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)较沪。三九已至鳞绕,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間尸曼,已是汗流浹背们何。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,010評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留控轿,地道東北人冤竹。 一個(gè)月前我還...
    沈念sama閱讀 46,433評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像茬射,于是被迫代替她去往敵國(guó)和親鹦蠕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,587評(píng)論 2 350