CMS 和 G1 算法
CMS GC (-XX:+UseConcMarkSweepGC)
并發(fā)標記掃描(Concurrent Mark Sweep挟憔,CMS)專為需要較短暫停的應(yīng)用而設(shè)計的老年代收集器链患,在應(yīng)用程序運行時需要與GC線程共享處理器資源总寒,所以需要運行在多核處理器上踩身。通常恃轩,在具有較多長生命周期數(shù)據(jù)集(老年代比較大)的應(yīng)用程序上使用篙程,從JDK 9 開始不推薦使用CMS收集器藏畅,推薦使用Garbage-First收集器
流程
每次major collection
怔蚌,CMS收集器會在收集開始時暫停所有應(yīng)用程序線程巩步,叫做initial mark pause,在并發(fā)跟蹤階段結(jié)束后會再次暫停桦踊,叫做remark pause椅野,第二次暫停往往時間較長,通常與minor collection
相當钞钙,兩個暫停期間都是多個線程執(zhí)行收集工作
- 初始標記 Initial Mark :Stop-The-World鳄橘,僅僅標記GC Roots直接關(guān)聯(lián)的對象,例如線程棧和寄存器1中引用的對象芒炼,靜態(tài)對象等瘫怜,速度很快
- 并發(fā)標記 Concurrent Mark:該階段中不需要暫停應(yīng)用程序,進行標記
- 重新標記 Remark:再次暫停應(yīng)用程序本刽,修正因為并發(fā)標記階段應(yīng)用線程運行導(dǎo)致的對象更新鲸湃,確保所有的可達對象都被標記,時間稍長
- 并發(fā)清除 Concurrent Sweep:執(zhí)行sweep子寓,來清除所有非可達對象所占用的內(nèi)存空間暗挑,存活的對象沒有移動,除非出現(xiàn)
Concurrent Mode Failure
斜友,觸發(fā)一次full GC炸裆,進行整理 - 重置 Resetting:清除數(shù)據(jù)結(jié)構(gòu),下一次并發(fā)收集做準備
Serial GC 和 CMS GC
由于應(yīng)用程序線程和垃圾收集器線程在major collection
期間并發(fā)運行鲜屏,因此垃圾收集器線程跟蹤的存活對象可能再收集過程結(jié)束時變得不可訪問。這些尚未回收的不可達對象被稱為浮動垃圾floating garbage。浮動垃圾的數(shù)量取決于并發(fā)收集周期的持續(xù)時間以及應(yīng)用程序的引用更新頻率(也稱為突變昙篙,mutation)。在下一次GC期間收集在當前并發(fā)收集周期結(jié)束時堆中的浮動垃圾
Concurrent Mode Failure
CMS收集器使用一個或多個與應(yīng)用程序線程同時運行的垃圾收集器線程酱吝,目標是在老年代變滿之前完成收集
在正常操作中,CMS收集器在應(yīng)用程序線程仍在運行時執(zhí)行大部分的跟蹤和清除工作土思,因此應(yīng)用程序線程只會有短暫的暫停务热。但是,如果CMS收集器無法在老年代填滿之前完成垃圾回收己儒,或者老年代中的可用空閑塊不能滿足分配需求崎岂,則所有應(yīng)用程序都將暫停,等待收集完成址愿。無法并發(fā)完成收集的情況稱為并發(fā)模式失敗该镣,表示需要調(diào)整CMS收集器參數(shù)
啟動時機
串行收集器只要老年代變滿就進行major collection
,在收集過程中停止所有應(yīng)用線程
CMS收集器中并發(fā)收集必須定時開始响谓,以便GC可以在老年代填滿之前完成,否則會產(chǎn)生并發(fā)模式故障Concurrent Mode Failure省艳,導(dǎo)致更長的暫停娘纷,CMS內(nèi)部會根據(jù)歷史記錄,對老年代內(nèi)存被填滿的剩余時間以及并發(fā)的收集時間進行估計跋炕,確保在老年代用盡之前完成GC赖晶,避免并發(fā)模式故障
由于要預(yù)留空間給浮動垃圾,如果老年代的內(nèi)存占用率超過CMS初始化占用比例辐烂,同樣也會觸發(fā)CMS遏插,調(diào)整觸發(fā)CMS的老年代百分比-XX:CMSInitiatingOccupancyFraction=<N>
缺點
- 占用更多的CPU和內(nèi)存資源
- 無法處理Floating Garbage,需要預(yù)留一定的空間
- 標記-清除算法纠修,不進行copy或者compact胳嘲,會產(chǎn)生碎片,連續(xù)空間不夠分配扣草,導(dǎo)致
concurrent mode failure
G1 (XX:+UseG1GC)
Garbage-First(G1) 垃圾收集器適用于具有大內(nèi)存的多處理器計算機了牛,它在延遲和吞吐量之間尋找一個最佳平衡,JDK 9開始作為默認GC
特點:
- 堆大小為10GB或更大辰妙,超過50%的Java堆被存活數(shù)據(jù)占用
- 對象分配和晉升的比率可能會隨著時間的推移顯著變化
- 堆中存在大量碎片
- 可預(yù)測的暫停時間鹰祸,不超過幾百毫秒,避免長時間的垃圾收集暫停
G1是一個分代收集收集器密浑,將堆分成新生代和老年代蛙婴,算法是并行的,大部分階段是并發(fā)運行尔破,部分操作會產(chǎn)生STW暫停街图,空間回收是逐步增量的浇衬,主要通過疏散的方式進行。G1通過跟蹤先前應(yīng)用程序行為和垃圾收集暫停的信息來建立相關(guān)成本的模型台夺,從而實現(xiàn)可預(yù)測性径玖,通過模型確定暫停時間內(nèi)要完成的工作量,首先在最有價值的區(qū)域中回收空間(即區(qū)域中大部分都是垃圾颤介,這也是G1名稱的由來)
G1主要通過疏散evacuation回收空間:在選定區(qū)域內(nèi)的活動對象會被復(fù)制到新的內(nèi)存區(qū)域梳星,同時在此過程中進行整理將對象放在一起,疏散完成后滚朵,先前占用的空間就可以回收利用
G1收集器有很高的概率冤灾,滿足設(shè)定的GC停頓時間,但是并不絕對完成辕近,所以G1不是一個實時的收集器(real-time collector)
堆布局
G1將堆分區(qū)為一組大小相等的區(qū)域韵吨,每個區(qū)域都是一個連續(xù)的虛擬內(nèi)存區(qū)域,區(qū)域是內(nèi)存分配和內(nèi)存回收的單位移宅,在某一時間归粉,這些區(qū)域可以是空的,或者分配給了年輕代或老年代
區(qū)域的大小通過-XX:G1HeapRegionSize
設(shè)定漏峰,默認區(qū)域大小基于堆的初始和最大大小糠悼,堆包含大約2048個區(qū)域。堆區(qū)域的大小可以在1到32 MB之間變化浅乔,但必須是2的冪次
Humongous Objects
巨型對象是大于或等于半個區(qū)域大小的對象
- 巨型對象都分配在老年代的一系列連續(xù)區(qū)域上倔喂,序列的最后一個區(qū)域中的剩余空間都不會進行分配
- 通常,巨型對象只能在mark過程的清理
Cleanup
暫停期間回收靖苇,或者在無法訪問時在Full GC期間回收席噩。 但是,對于大型對象有特殊的規(guī)定贤壁,用于原始類型的數(shù)組悼枢,例如bool,各種整數(shù)和浮點值芯砸。 如果在任何類型的垃圾回收暫停中許多對象都未引用大型對象萧芙,則G1機會嘗試回收大型對象。 默認情況下啟用此行為假丧,但是您可以使用選項-XX:G1EagerReclaimHumongousObjects禁用它双揪。 - 巨型對象的分配可能導(dǎo)致垃圾收集暫停過早發(fā)生,每次巨型對象分配時G1都會檢查初始堆占用閾值包帚,并且如果當前占用率超過該閾值渔期,則立即開始新生代收集初始標記
- 即使在Full GC期間,這些巨型對象也不會移動,這可能會導(dǎo)致過早的Full GC疯趟,或者由于區(qū)域空間的碎片而導(dǎo)致剩余大量可用空間但是內(nèi)存不足的情況
垃圾收集周期
G1收集器在兩個階段之間交替:
- young-only phase:期初是進行正常的年輕代收集
Normal young collections
拘哨,晉升對象到老年代,當老年代的占用率到達堆占用閾值信峻,進行并發(fā)收集Concurrent Start young collection
倦青,步驟如下- 并發(fā)啟動
Concurrent Start
:在執(zhí)行Normal young collections
的STW時進行Initial mark
,然后啟動并發(fā)標記Concurrent marking
盹舞,并發(fā)標記確定老年代區(qū)域中當前可到達(存活)的所有對象产镐,以便空間清理階段保留。收集標記進行過程中踢步,也會出現(xiàn)正常的代收集癣亚,即可以被年輕代的STW中斷。標記完成后還有兩個STW停頓:重新標記和清理 - 重新標記
Remark
:通過STW最終確定標記获印,執(zhí)行全局引用處理和類卸載述雾,回收完全空白的區(qū)域并清理內(nèi)部數(shù)據(jù)結(jié)構(gòu)。在重新標記和清理之間兼丰,G1會并發(fā)計算所選老年代區(qū)域中能夠回收的可用空間 - 清理
Cleanup
:這是標記的最后階段玻孟,STW決定是否執(zhí)行space-reclamation
,如果執(zhí)行鳍征,會進行一次Prepare Mixed young collection
- 并發(fā)啟動
- space-reclamation phase:空間回收階段由多個混合收集
Mixed collections
組成取募,除了處理新生代之外,老年代的空間也會進行疏散蟆技,當G1確定疏散更多的老年代區(qū)域產(chǎn)生的自由空間不值得努力時,空間回收階段結(jié)束
如果應(yīng)用程序在垃圾回收時內(nèi)存不足斗忌,則G1會像其他收集器一樣STW质礼,執(zhí)行Full GC
Collection Set:將要被GC收集的region集合,內(nèi)部的存活對象在GC期間都會被疏散(復(fù)制/移動)
在young-only
階段织阳,收集的區(qū)域集合(Collection Sets)僅由新生代區(qū)域組成眶蕉,G1每次在年輕代收集的末尾計算新生代的大小,在下一個階段改變唧躲。如果沒有另外約束造挽,則G1自適應(yīng)地在-XX:G1NewSizePercent
和-XX:G1MaxNewSizePercent
之間調(diào)整大小來滿足暫停時間的要求
在space-reclamation
階段,G1試圖最大化回收老年代的內(nèi)存弄痹,按照回收的效率高低清理老年代區(qū)域饭入,同時通過剩余可用時間確定最終收集區(qū)域的大小,活動對象比例在-XX:G1MixedGCLiveThresholdPercent
閾值之上的不進行清理肛真,-XX:G1MixedGCCountTarget
設(shè)定該階段中進行Mixed垃圾收集的次數(shù)
當收集集候選區(qū)域(collection set candidate regions)中可回收的空間量小于-XX:G1HeapWastePercent
設(shè)置的百分比時谐丢,結(jié)束該階段
確定啟動堆占用率
啟動堆占用百分比(Initiating Heap Occupancy Percent, IHOP)是觸發(fā)初始標收集的閾值,定義為老年代大小的百分比
G1默認情況下通過觀察標記所花費的時間以及在標記周期內(nèi)老年代分配的內(nèi)存數(shù)量來自動確定最佳的IHOP,此功能稱為自適應(yīng)IHOP乾忱。如果此功能處于活動狀態(tài)讥珍,-XX:InitiatingHeapOccupancyPercent
只有在沒有足夠的觀察值來進行預(yù)測時使用,選項-XX:-G1UseAdaptiveIHOP
可以關(guān)閉G1的自適應(yīng)調(diào)整窄瘟,此時一直通過-XX:InitiatingHeapOccupancyPercent
確定閾值
標記
G1標記使用Snapshot-At-The-Beginning (SATB) 算法衷佃。 在Initial Mark
暫停時獲取堆的虛擬快照,在標記開始時存活的所有對象在后來的標記過程中都認為是存活對象蹄葱。這意味著在標記期間變?yōu)樗劳觯o法訪問)的對象在空間回收階段仍然被認為存活氏义,進行疏散 ,與其他收集器相比新蟆,這可能會導(dǎo)致錯誤地額外保留一些內(nèi)存觅赊。但是,SATB可以降低Remark階段的延遲