JVM(十一)內(nèi)存與垃圾回收|垃圾回收器

本文介紹垃圾回收器钮呀。

目錄
?1 GC的分類與性能指標(biāo)
? 2 評估GC的性能指標(biāo)
??2.1 吞吐量
??2.2 暫停時間
?? 2.3 吞吐量VS暫停時間
?3 不同的垃圾回收器概述
?? 3.1 垃圾收集器發(fā)展史
?? 3.2 7款經(jīng)典的垃圾回收器
?? 3.3 7款經(jīng)典的垃圾收集器與垃圾分代之間的關(guān)系
?? 3.4 垃圾收集器的組合關(guān)系
?? 3.5 查看默認(rèn)的垃圾收集器
?4 Serial回收器:串行回收
?5 ParNew回收器:并行回收
?6 Parallel回收器:吞吐量優(yōu)先
?7 CMS回收器:低延遲
?8 G1回收器:區(qū)域化分代式
?? 8.1 為什么要發(fā)布Garbage First (G1)GC剑鞍?
?? 8.2 為什么叫做Garbage First (G1)GC?
?? 8.3 優(yōu)勢(特點(diǎn))
?? 8.4 缺點(diǎn)
?? 8.5 參數(shù)設(shè)置
?? 8.6 G1回收器的常見操作步驟
?? 8.7 適用場景
?? 8.8 region分區(qū)
?? 8.9 G1回收器垃圾回收過程
?? 8.10 記憶集與寫屏障
?? 8.11 G1回收過程詳解
?? 8.12 優(yōu)化建議
?9 垃圾回收器總結(jié)
?10 GC日志分析
?11 垃圾回收器的新發(fā)展


1 GC的分類與性能指標(biāo)


  • 垃圾收集器沒有在規(guī)范中進(jìn)行過多的規(guī)定爽醋,可以由不同的廠商蚁署、不同版本的JVM來實現(xiàn)。
  • 由于JDK的版本處于高速迭代過程中子房,因此Java發(fā)展至今已經(jīng)衍生了眾多的GC版本形用。
  • 從不同角度分析垃圾收集器,可以將GC分為不同的類型证杭。

按線程數(shù)分田度,可以分為串行垃圾回收器和并行垃圾回收器

  • 串行回收指的是在同一時間段內(nèi)只允許有一個CPU用于執(zhí)行垃圾回收操作,此時工作線程被暫停解愤,直至垃圾收集工作結(jié)束镇饺。
    • 在諸如單CPU處理器或者較小的應(yīng)用內(nèi)存等硬件平臺不是特別優(yōu)越的場合,串行回收器的性能表現(xiàn)可以超過并行回收器和并發(fā)回收器送讲。所以奸笤,串行回收默認(rèn)被應(yīng)用在客戶端的Client模式下的JVM中
    • 在并發(fā)能力比較強(qiáng)的CPU上,并行回收器產(chǎn)生的停頓時間要短于串行回收器洛二。
  • 和串行回收相反咨堤,并行收集可以運(yùn)用多個CPU同時執(zhí)行垃圾回收督怜,因此提升 了應(yīng)用的吞吐量,不過并行回收仍然與串行回收一樣健盒,采用獨(dú)占式,使用了“ Stop-the-world”機(jī)制称簿。

按照工作模式分扣癣,可以分為并發(fā)式垃圾回收器和獨(dú)占式垃圾回收器

  • 并發(fā)式垃圾回收器與應(yīng)用程序線程交替工作,以盡可能減少應(yīng)用程序的停頓時間憨降。
  • 獨(dú)占式垃圾回收器(Stop the world)一旦運(yùn)行父虑,就停止應(yīng)用程序中的所有用戶線程,直到垃圾回收過程完全結(jié)束授药。


按碎片處理方式分士嚎,可分為壓縮式垃圾回收器和非壓縮式垃圾回收器

  • 壓縮式垃圾回收器會在回收完成后,對存活對象進(jìn)行壓縮整理悔叽,消除回收后的碎片航邢。
    • 再分配對象空間使用: 指針碰撞
  • 非壓縮式的垃圾回收器不進(jìn)行這步操作。
    • 再分配對象空間使用: 空閑列表

按工作的內(nèi)存區(qū)間分骄蝇,又可分為年輕代垃圾回收器和老年代垃圾回收器

2 評估GC的性能指標(biāo)


  • 吞吐量:運(yùn)行用戶代碼的時間占總運(yùn)行時間的比例

    • (總運(yùn)行時間:程序的運(yùn)行時間十內(nèi)存回收的時間)
  • 垃圾收集開銷:吞吐量的補(bǔ)數(shù)膳殷,垃圾收集所用時間與總運(yùn)行時間的比例。

  • 暫停時間:執(zhí)行垃圾收集時九火,程序的工作線程被暫停的時間

  • 收集頻率:相對于應(yīng)用程序的執(zhí)行赚窃,收集操作發(fā)生的頻率。

  • 內(nèi)存占用: Java堆區(qū)所占的內(nèi)存大小

  • 快速:一個對象從誕生到被回收所經(jīng)歷的時間岔激。

  • 這三者(加粗部分)共同構(gòu)成一個“不可能三角”勒极。三者總體的表現(xiàn)會隨著技術(shù)進(jìn)步而越來越好。一款優(yōu)秀的收集器通常最多同時滿足其中的兩項虑鼎。

  • 這三項里辱匿,暫停時間的重要性日益凸顯键痛。因為隨著硬件發(fā)展,內(nèi)存占用多匾七,硬件性能的提升也有助于降低收集器運(yùn)行時對應(yīng)用程序的影響絮短,即提高了吞吐量。而內(nèi)存的擴(kuò)大昨忆,對延遲反而帶來負(fù)面效果丁频。

  • 簡單來說,主要抓住兩點(diǎn):

    • 吞吐量
    • 暫停時間

2.1 吞吐量

  • 吞吐量就是CPU用于運(yùn)行用戶代碼的時間與CPU總消耗時間的比值邑贴,即吞吐量=運(yùn)行用戶代碼時間/ (運(yùn)行用戶代碼時間+垃圾收集時間)
    • 比如:虛擬機(jī)總共運(yùn)行了100分鐘席里,其中垃圾收集花掉1分鐘,那吞吐量就是99%
  • 這種情況下拢驾,應(yīng)用程序能容忍較高的暫停時間奖磁,因此,高吞吐量的應(yīng)用程序有更長的時間基準(zhǔn)繁疤,快速響應(yīng)是不必考慮的署穗。
  • 吞吐量優(yōu)先,意味著在單位時間內(nèi)嵌洼,STW的時間最短: 0.2 + 0.2 = 0.4


2.2 暫停時間

  • “暫停時間”是指一個時間段內(nèi)應(yīng)用程序線程暫停案疲,讓GC線程執(zhí)行的狀態(tài)
    • 例如,GC期間100毫秒的暫停時間意味著在這100毫秒期間內(nèi)沒有應(yīng)用程序線程是活動的麻养。.
  • 暫停時間優(yōu)先褐啡,意味著盡可能讓單次STW的時間最短: 0.1 + 0.1 + 0.1 + 0.1+0.1=0.5

2.3 吞吐量VS暫停時間

  • 高吞吐量較好因為這會讓應(yīng)用程序的最終用戶感覺只有應(yīng)用程序線程在做“生產(chǎn)性”工作。直覺上鳖昌,吞吐量越高程序運(yùn)行越快备畦。
  • 低暫停時間(低延遲)較好因為從最終用戶的角度來看不管是GC還是其他原因?qū)е乱粋€應(yīng)用被掛起始終是不好的。這取決于應(yīng)用程序的類型许昨,有時候甚至短暫的200毫秒暫停都可能打斷終端用戶體驗懂盐。因此,具有低的較大暫停時間是非常重要的糕档,特別是對于一個交互式應(yīng)用程序莉恼。
  • 不幸的是”高吞吐量”和”低暫停時間”是一對相互競爭的目標(biāo)(矛盾)。
    • 因為如果選擇以吞吐量優(yōu)先速那,那么必然需要降低內(nèi)存回收的執(zhí)行頻率俐银,但是這樣會導(dǎo)致GC需要更長的暫停時間來執(zhí)行內(nèi)存回收。
    • 相反的端仰,如果選擇以低延遲優(yōu)先為原則捶惜,那么為了降低每次執(zhí)行內(nèi)存回收時的暫停時間,也只能頻繁地執(zhí)行內(nèi)存回收荔烧,但這又引起了年輕代內(nèi)存的縮誠和導(dǎo)致程序吞吐量的下降吱七。
  • 在設(shè)計(或使用) GC算法時汽久,我們必須確定我們的目標(biāo): 一個GC算法只可能針對兩個目標(biāo)之一(即只專注于較大吞吐量或最小暫停時間),或嘗試找到一個二者的折衷踊餐。
  • 現(xiàn)在標(biāo)準(zhǔn):在最大吞吐量優(yōu)先的情況下景醇,降低停頓時間

3 不同的垃圾回收器概述


3.1 垃圾收集器發(fā)展史

有了虛擬機(jī)市袖,就一定需要收集垃圾的機(jī)制啡直,這就是Garbage Collection烁涌, 對應(yīng)的產(chǎn)品我們稱為Garbage Collector.

  • 1999年隨JDK1.3.1一 起來的是串行方式的Serial GC苍碟,它是第一款GC。ParNew垃圾收集器是Serial收集器的多線程版本
  • 2002年2月26日撮执,Parallel GC和Concurrent Mark Sweep GC跟隨JDK1.4.2一起發(fā)布
  • Parallel GC在JDK6之后成為HotSpot默認(rèn)GC微峰。
  • 2012年,在JDK1.7u4版本中抒钱,G1可用蜓肆。
  • 2017年,JDK9中G1變成默認(rèn)的垃圾收集器谋币,以替代CMS仗扬。
  • 2018年3月,JDK10中G1垃圾回收器的并行完整垃圾回收蕾额,實現(xiàn)并行性來改善最壞情況下的延遲早芭。
  • 2018年9月,JDK11發(fā)布诅蝶。引入Epsilon垃圾回收器退个,又被稱為"No-op (無操作) "回收器。同時调炬,引入ZGC:可伸縮的低延遲垃圾回收器(Experimental)语盈。
  • 2019年3月,JDK12發(fā)布缰泡。 增強(qiáng)G1刀荒,自動返回未用堆內(nèi)存給操作系統(tǒng)。同時棘钞,引入Shenandoah GC:低停頓時間的GC (Experimental)照棋。
  • 2019年9月,JDK13發(fā)布武翎。增強(qiáng)ZGC烈炭,自動返回未用堆內(nèi)存給操作系統(tǒng)。
  • 2020年3月宝恶,JDK14發(fā)布符隙。刪除CMS垃圾回收器趴捅。擴(kuò)展ZGC在macOS和Windows上的應(yīng)用

3.2 7款經(jīng)典的垃圾收集器

  • 串行回收器:Serial. Serial Old

  • 并行回收器:ParNew. Parallel Scavenge. Parallel Old

  • 并發(fā)回收器:CMS. G1

3.3 7款經(jīng)典的垃圾收集器與垃圾分代之間的關(guān)系

  • 新生代收集器: Serial、 ParNeW霹疫、Parallel Scavenge拱绑;

  • 老年代收集器: Serial 0ld、 Parallel 0ld丽蝎、 CMS猎拨;

  • 整堆收集器: G1;


3.4 垃圾收集器的組合關(guān)系

  • 兩個收集器間有連線屠阻,表明它們可以搭配使用: Serial/Serial old红省、Serial/CMS、 ParNew/Serial old国觉、ParNew/CMS吧恃、 Parallel Scavenge/Serial old、Parallel Scavenge/Parallel old麻诀、G1痕寓;
  • 其中Serial old作為CMS 出現(xiàn)"Concurrent Mode Failure"失敗的后 備預(yù)案。
  • (紅色虛線)由于維護(hù)和兼容性測試的成本蝇闭,在JDK 8時將Serial+CMS呻率、 ParNew+Serial old這兩個組合聲明為廢棄(JEP 173),并在JDK 9中完全取消了這些組合的支持(JEP214)呻引,即:移除礼仗。
  • (綠色虛線)JDK 14中:棄用Parallel Scavenge和Serial old GC組合(JEP366 )
  • (青色虛線)JDK 14中:刪除CMS垃圾回收器 (JEP 363)

?? 為什么要有很多收集器個不夠嗎? 因為Java的使用場景很多苞七, 移動端藐守,服務(wù)器等。所以就需要針對不同的場景蹂风,提供不同的垃圾收集器卢厂,提高垃圾收集的性能。
??雖然我們會對各個收集器進(jìn)行比較惠啄,但并非為了挑選一個最好的收集器出來慎恒。沒有一種放之四海皆準(zhǔn)、任何場景下都適用的完美收集器存在撵渡,更加沒有萬能的收集器融柬。所以我們選擇的只是對具體應(yīng)用最合適的收集器

3.5 查看默認(rèn)的垃圾收集器

  • -xx:+PrintCommandLineFlags: 查看命令行相關(guān)參數(shù)(包含使用的垃圾收集器)
/**
 *  -XX:+PrintCommandLineFlags 
 *
 * 修改默認(rèn)GC
 *  -XX:+UseSerialGC:表明新生代使用Serial GC 趋距,同時老年代使用Serial Old GC
 *
 *  -XX:+UseParNewGC:標(biāo)明新生代使用ParNew GC
 *
 *  -XX:+UseParallelGC:表明新生代使用Parallel GC
 *  -XX:+UseParallelOldGC : 表明老年代使用 Parallel Old GC
 *  說明:二者可以相互激活
 *
 *  -XX:+UseConcMarkSweepGC:表明老年代使用CMS GC粒氧。同時,年輕代會觸發(fā)對ParNew 的使用
 */
public class GCUseTest {
    public static void main(String[] args) {
        ArrayList<byte[]> list = new ArrayList<>();

        while(true){
            byte[] arr = new byte[100];
            list.add(arr);
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 使用命令行指令:jinfo 一flag相關(guān)垃圾回收器參數(shù)進(jìn)程ID
    jdk8 默認(rèn)使用的是parallel

4 Serial回收器:串行回收


  • Serial收集器是最基本节腐、歷史最悠久的垃圾收集器了外盯。JDK1.3之前回收新生代唯一的選擇摘盆。
  • Serial收集器作為HotSpot中Client模式下的默認(rèn)新生代垃圾收集器。
  • Serial收集器采用復(fù)制算法饱苟、串行回收Stop-the-World機(jī)制的方式執(zhí)行內(nèi)存回收孩擂。
  • 除了年輕代之外,Serial收集器還提供用于執(zhí)行老年代垃圾收集的Serial old收集器箱熬。 Serial old收集器同樣也采用了串行回收Stop the World機(jī)制类垦,只不過內(nèi)存回收算法使用的是標(biāo)記-壓縮算法
    • Serial Old是運(yùn)行在Client模式下默認(rèn)的老年代的垃圾回收器
    • Serial Old在Server模式下主要有兩個用途:①與新生代的ParallelScavenge配合使用; ②作為老年代CMS收集器的后備垃圾收集方案
  • 這個收集器是一個單線程的收集器城须,但它的“單線程”的意義并不僅僅說明它只會使用一個CPU或一條收集線程去完成垃圾收集工作蚤认,更重要的是在它進(jìn)行垃圾收集時,必須暫停其他所有的工作線程酿傍,直到它收集結(jié)束(Stop The World )烙懦。

優(yōu)勢

  • 簡單而高效(與其他收集器的單線程比)驱入,對于限定單個CPU的環(huán)境來說赤炒,Serial收集器由于沒有線程交互的開銷,專心做垃圾收集自然可以獲得最高的單線程收集效率亏较。
    • 運(yùn)行在Client模式下的虛擬機(jī)是個不錯的選擇莺褒。
  • 在用戶的桌面應(yīng)用場景中,可用內(nèi)存一般不大(幾十MB至一兩百M(fèi)B)雪情, 可以在較短時間內(nèi)完成垃圾收集(幾十ms至一百多ms) 遵岩,只要不頻繁發(fā)生,使用串行回收器是可以接受的巡通。
  • 在HotSpot虛擬機(jī)中尘执,使用-XX:+UseSerialGC 參數(shù)可以指定年輕代和老年代都使用串行收集器。
    • 等價于新生代用Serial GC宴凉,且老年代用Serial Old GC

總結(jié)

  • 這種垃圾收集器了解即可誊锭,現(xiàn)在已經(jīng)不用串行的了。而且它限定單核cpu才可以用∶殖現(xiàn)在都不是單核的了丧靡。
  • 對于交互較強(qiáng)的應(yīng)用而言,這種垃圾收集器是不能接受的籽暇。一般在Javaweb應(yīng)用程序中是不會采用串行垃圾收集器的温治。

5 ParNew回收器:并行回收


  • 如果說Serial GC是年輕代中的單線程垃圾收集器,那么ParNew收集器則是Serial收集器的多線程版本戒悠。

    • Par是Parallel的縮寫熬荆,New: 只能處理的是新生代
  • ParNew收集器除了采用并行回收的方式執(zhí)行內(nèi)存回收外,兩款垃圾收集器之間幾乎沒有任何區(qū)別绸狐。ParNew收集器在年輕代中同樣也是采用復(fù)制算法卤恳、Stop-the-World機(jī)制捏顺。

  • ParNew是很多JVM運(yùn)行在Server模式下新生代的默認(rèn)垃圾收集器。

    • 對于新生代纬黎,回收次數(shù)頻繁幅骄,使用并行方式高效。
    • 對于老年代本今,回收次數(shù)少拆座,使用串行方式節(jié)省資源。(CPU并行 需要切換線程冠息,串行可以省去切換線程的資源)
  • 由于ParNew收集器是基于并行回收挪凑,那么是否可以斷定ParNew收集器的回收效率在任何場景下都會比Serial收集器更高效?

    • ParNew 收集器運(yùn)行在多CPU的環(huán)境下逛艰,由于可以充分利用多CPU躏碳、 多核心等物理硬件資源優(yōu)勢,可以更快速地完成垃圾收集散怖,提升程序的吞吐量菇绵。
    • 但是在單個CPU的環(huán)境下,ParNew收集器不比Serial收集器更高效镇眷。雖然Serial收集器是基于串行回收咬最,但是由于CPU不需要頻繁地做任務(wù)切換,因此可以有效避免多線程交互過程中產(chǎn)生的一些額外開銷欠动。
  • 除Serial外永乌,目前只有ParNew GC能與CMS收集器配合工作

  • 在程序中,開發(fā)人員可以通過選項-XX:+UseParNewGC手動指定使用ParNew收集器執(zhí)行內(nèi)存回收任務(wù)具伍。它表示年輕代使用并行收集器翅雏,不影響老年代。

  • -XX:ParallelGCThreads 限制線程數(shù)量人芽,默認(rèn)開啟和CPU數(shù)據(jù)相同的線程數(shù)望几。.

6 Parallel回收器:吞吐量優(yōu)先


  • HotSpot的年輕代中除了擁有ParNew收集器是基于并行回收的以外, Parallel Scavenge收集器同樣也采用了復(fù)制算法啼肩、并行回收Stop the World機(jī)制橄妆。
  • 那么Parallel收集器的出現(xiàn)是否多此一舉?
    • 和ParNew收集器不同祈坠,Parallel Scavenge收集 器的目標(biāo)則是達(dá)到一個可控制的吞吐量(Throughput)害碾,它也被稱為吞吐量優(yōu)先的垃圾收集器。
    • 自適應(yīng)調(diào)節(jié)策略也是Parallel Scavenge 與ParNew一個重要區(qū)別赦拘。
  • 高吞吐量則可以高效率地利用CPU 時間慌随,盡快完成程序的運(yùn)算任務(wù),主要適合在后臺運(yùn)算而不需要太多交互的任務(wù)。因此阁猜,常見在服務(wù)器環(huán)境中使用丸逸。例如,那些執(zhí)行批量處理剃袍、訂單處理黄刚、工資支付、科學(xué)計算的應(yīng)用程序民效。
  • Parallel收集器在JDK1.6時提供了用于執(zhí)行老年代垃圾收集的 Parallel Old收集器憔维,用來代替老年代的Serial Old收集器。
  • Parallel Old收集器采用了標(biāo)記-壓縮算法畏邢,但同樣也是基于并行回收Stop-the-World機(jī)制业扒。
  • 在程序吞吐量優(yōu)先的應(yīng)用場景中,Parallel 收集器和Parallel Old收集器的組合舒萎,在Server模式下的內(nèi)存回收性能很不錯程储。
  • 在Java8中,默認(rèn)是此垃圾收集器

參數(shù)配置

  • -XX:+UseParallelGC手動指定 年輕代使用Parallel并行收集器執(zhí)行內(nèi)存回收任務(wù)臂寝。
  • -XX:+UseParallelOldGc手動指定老年代都是使用并行回收收集器章鲤。
    • 分別適用于新生代和老年代。默認(rèn)jdk8是開啟的交煞。
    • 上面兩個參數(shù)咏窿,默認(rèn)開啟一個斟或,另一個也會被開啟素征。 (互相激活)
  • -XX:ParallelGCThreads設(shè)置年輕代并行收集器的線程數(shù)。一般地萝挤,最好與CPU數(shù)量相等御毅,以避免過多的線程數(shù)影響垃圾收集性能。
    • 在默認(rèn)情況下怜珍,當(dāng)CPU數(shù)量小于8個端蛆, Paralle lGCThreads 的值等于CPU數(shù)量。
    • 當(dāng)CPU數(shù)量大于8個酥泛, ParallelGCThreads的值等于3+[5*CPU_ Count]/8]
  • -XX :MaxGCPauseMillis設(shè)置垃圾收集器最大停頓時間(即STW的時間)今豆。單位是毫秒。
    • 為了盡可能地把停頓時間控制在MaxGCPauseMills以內(nèi)柔袁,收集器在.工作時會調(diào)整Java堆大小或者其他一些參數(shù)呆躲。
    • 對于用戶來講,停頓時間越短體驗越好捶索。但是在服務(wù)器端插掂,我們注重 高并發(fā),整體的吞吐量。所以服務(wù)器端適合Parallel進(jìn)行控制辅甥。該參數(shù)使用需謹(jǐn)慎酝润。
  • -XX:GCTimeRatio垃圾收集時間占總時間的比例(= 1 / (N + 1))用于衡量吞吐量的大小。
    • 取值范圍(0璃弄, 100)要销。默認(rèn)值99。
    • 與前一個-XX:MaxGCPauseMillis參數(shù)有一定矛盾性夏块。暫停時間越長蕉陋,Radio參數(shù)就容易超過設(shè)定的比例。
  • -XX:+UseAdaptiveSizePolicy設(shè) 置Parallel Scavenge收集器具有自適應(yīng)調(diào)節(jié)策略
    • 在這種模式下拨扶,年輕代的大小凳鬓、Eden和Survivor的比例、晉升老年 代的對象年齡等參數(shù)會被自動調(diào)整患民,已達(dá)到在堆大小缩举、吞吐量和停頓時間之間的平衡點(diǎn)。
    • 在手動調(diào)優(yōu)比較困難的場合匹颤,可以直接使用這種自適應(yīng)的方式仅孩,僅指 定虛擬機(jī)的最大堆、目標(biāo)的吞吐量(GCTimeRatio)和停頓時間(MaxGCPauseMills)印蓖,讓虛擬機(jī)自己完成調(diào)優(yōu)工作辽慕。

7 CMS回收器:低延遲


  • 在JDK1.5時期, HotSpot推出了一款在強(qiáng)交互應(yīng)用中幾乎可認(rèn)為有劃時代意義的垃圾收集器: CMS (Concurrent -Mark- Sweep)收集器赦肃,這款收集器是HotSpot虛擬機(jī)中第一款真正意義上的并發(fā)收集器溅蛉,它第一次實現(xiàn)了讓垃圾收集線程與用戶線程同時工作
  • CMS收集器的關(guān)注點(diǎn)是盡可能縮短垃圾收集時用戶線程的停頓時間他宛。停頓時 間越短(低延遲)就越適合與用戶交互的程序船侧,良好的響應(yīng)速度能提升用戶體驗。
    • 目前很大一部分的Java應(yīng)用集中在互聯(lián)網(wǎng)站或者B/S系統(tǒng)的服務(wù)端上厅各,這類應(yīng)用尤其重視服務(wù)的響應(yīng)速度镜撩,希望系統(tǒng)停頓時間最短,以給用戶帶來較好的體驗队塘。CMS收集器就非常符合這類應(yīng)用的需求袁梗。
  • CMS的垃圾收集算法采用標(biāo)記-清除算法,并且也 會stop-the-world
  • 不幸的是憔古,CMS 作為老年代的收集器遮怜,卻無法與JDK 1.4.0 中已經(jīng)存在的新生代收集器Parallel Scavenge配合工作,所以在JDK 1. 5中使用CMS來收集老年代的時候投放,新生代只能選擇ParNew或者Serial收集器中的一個奈泪。
  • 在G1出現(xiàn)之前,CMS使用還是非常廣泛的。一直到今天涝桅,仍然有很多系統(tǒng)使用CMS GC拜姿。

垃圾收集過程

??CMS整個過程比之前的收集器要復(fù)雜,整個過程分為4個主要階段冯遂,即初始標(biāo)記階段蕊肥、并發(fā)標(biāo)記階段重新標(biāo)記階段并發(fā)清除階段蛤肌。

  • 初始標(biāo)記(Initial-Mark) 階段:在這個階段中壁却,程序中所有的工作線程都將會因為. “Stop-the-World"機(jī)制而出現(xiàn)短暫的暫停,這個階段的主要任務(wù)僅僅只是標(biāo)記出GCRoots能直接關(guān)聯(lián)到的對象裸准。一旦標(biāo)記完成之后就會恢復(fù)之前被暫停的所有應(yīng)用.線程展东。由于直接關(guān)聯(lián)對象比較小,所以這里的速度非吵淳悖快盐肃。
  • 并發(fā)標(biāo)記(Concurrent-Mark)階段:從GC Roots的 直接關(guān)聯(lián)對象開始遍歷整個對象圖的過程,這個過程耗時較長但是不需要停頓用戶線程权悟,可以與垃圾收集線程一起并發(fā)運(yùn)行砸王。
  • 重新標(biāo)記(Remark) 階段:由于在并發(fā)標(biāo)記階段中,程序的工作線程會和垃圾收集線程同時運(yùn)行或者交叉運(yùn)行峦阁,因此為了修正并發(fā)標(biāo)記期間谦铃,因用戶程序繼續(xù)運(yùn)作而導(dǎo)致標(biāo)記產(chǎn)生變動的那一部分對象的標(biāo)記記錄,這個階段的停頓時間通常會比初始標(biāo)記階段稍長一些榔昔,但也遠(yuǎn)比并發(fā)標(biāo)記階段的時間短驹闰。
  • 并發(fā)清除( Concurrent-Sweep)階段:此階段清理刪除掉標(biāo)記階段判斷的已經(jīng)死亡的對象,釋放內(nèi)存空間件豌。由于不需要移動存活對象疮方,所以這個階段也是可以與用戶線程同時并發(fā)的

??盡管CMS收集器采用的是并發(fā)回收(非獨(dú)占式),但是在其初始化標(biāo)記和再次標(biāo)記這兩個階段中仍然需要執(zhí)行“Stop-the-World”機(jī)制暫停程序中的工作線程茧彤,不過暫停時間并不會太長,因此可以說明目前所有的垃圾收集器都做不到完全不需要“Stop-the-World”疆栏,只是盡可能地縮短暫停時間曾掂。
??由于最耗費(fèi)時間的并發(fā)標(biāo)記與并發(fā)清除階段都不需要暫停工作,所以整體的回收是低停頓的壁顶。
??另外珠洗,由于在垃圾收集階段用戶線程沒有中斷,所以在CMS回收過程中若专,還應(yīng)該確保應(yīng)用程序用戶線程有足夠的內(nèi)存可用。因此,CMS收集器不能像其他收集器那樣等到老年代幾乎完全被填滿了再進(jìn)行收集隅忿,而是當(dāng)堆內(nèi)存使用率達(dá)到某一閾值時,便開始進(jìn)行回收自阱,以確保應(yīng)用程序在CMS工作過程中依然有足夠的空間支持應(yīng)用程序運(yùn)行。要是CMS運(yùn)行期間預(yù)留的內(nèi)存無法滿足程序需要米酬,就會出現(xiàn)一次“Concurrent Mode Failure”失敗沛豌,這時虛擬機(jī)將啟動后備預(yù)案:臨時啟用Serial Old收集器來重新進(jìn)行老年代的垃圾收集,這樣停頓時間就很長了赃额。
??CMS收集器的垃圾收集算法采用的是標(biāo)記--清除算法加派,這意味著每次執(zhí)行完內(nèi)存回收后,由于被執(zhí)行內(nèi)存回收的無用對象所占用的內(nèi)存空間極有可能是不連續(xù)的一些內(nèi)存塊跳芳,不可避免地將會產(chǎn)生一些內(nèi)存碎片芍锦。 那么CMS在為新對象分配內(nèi)存空間時,將無法使用指針碰撞(Bump the Pointer) 技術(shù)飞盆,而只能夠選擇空閑列表(Free List) 執(zhí)行內(nèi)存分配醉旦。

有人會覺得既然Mark Sweep會造成內(nèi)存碎片,那么為什么不把算法換成Mark Compact呢桨啃?
因為當(dāng)并發(fā)清除的時候车胡,用Compact整理內(nèi)存的話,原來的用戶線程使用的內(nèi)存還怎么用呢照瘾?要保證用戶線程能繼續(xù)執(zhí)行匈棘,前提的它運(yùn)行的資源不受影響嘛。Mark Compact更適合“Stop the World”這種場景”下使用

CMS的優(yōu)點(diǎn) .

  • 并發(fā)收集
  • 低延遲

CMS的弊端

  • 會產(chǎn)生內(nèi)存碎片析命,導(dǎo)致并發(fā)清除后主卫,用戶線程可用的空間不足。在無法分配大對象的情況下鹃愤,不得不提前觸發(fā)Full GC簇搅。
  • CMS收集器對CPU資源非常敏感。在并發(fā)階段软吐,它雖然不會導(dǎo)致用戶停頓瘩将,但是會因為占用了一部分線程而導(dǎo)致應(yīng)用程序變慢,總吞吐量會降低凹耙。
  • CMS收集器無法處理浮動垃圾姿现。# CMS垃圾收集器——重新標(biāo)記和浮動垃圾的思考

參數(shù)設(shè)置

  • -XX:+UseConcMarkSweepGc 手動指定使用CMS收集器執(zhí)行內(nèi)存回收任務(wù)。
    • 開啟該參數(shù)后會自動將-XX:+UseParNewGc打開肖抱。即: ParNew (Young區(qū)用) +CMS (Old區(qū)用) +Serial Old的組合备典。
  • -XX:CMS1ni tiatingOccupanyFraction設(shè)置堆內(nèi)存使用率的閾值,一旦達(dá)到該閾值意述,便開始進(jìn)行回收提佣。
    • JDK5及以前版本的默認(rèn)值為68吮蛹,即當(dāng)老年代的空間使用率達(dá)到68%時,會執(zhí)行一 次CMS 回收拌屏。 JDK6及以上版本默認(rèn)值為92%
    • 如果內(nèi)存增長緩慢潮针,則可以設(shè)置一個稍大的值,大的閾值可以有效降低CMS的觸發(fā)頻率槐壳,減少老年代回收的次數(shù)可以較為明顯地改善應(yīng)用程序性能然低。反之,如果應(yīng)用程序內(nèi)存使用率增長很快务唐,則應(yīng)該降低這個閾值雳攘,以避免頻繁觸發(fā)老年代串行收集器。因此通過該選項便可以有效降低Full GC的執(zhí)行次數(shù)枫笛。
  • -XX:+UseCMSCompactAtFullCollection用于指定在執(zhí)行完Full GC后對內(nèi)存空間進(jìn)行壓縮整理吨灭,以此避免內(nèi)存碎片的產(chǎn)生。不過由于內(nèi)存壓縮整理過程無法并發(fā)執(zhí)行刑巧,所帶來的問題就是停頓時間變得更長了喧兄。
  • -XX:CMSFullGCsBeforeCompaction設(shè)置在執(zhí)行多少次Full GC后對內(nèi)存空間進(jìn)行壓縮整理。
  • -XX:ParallelCMSThreads 設(shè)置CMS的線程數(shù)量啊楚。
    • CMS 默認(rèn)啟動的線程數(shù)是(ParallelGCThreads+3) /4吠冤, ParallelGCThreads是年輕代并行收集器的線程數(shù)。當(dāng)CPU資源比較緊張時恭理,受到CMs收集器線程的影響拯辙,應(yīng)用程序的性能在垃圾回收階段可能會非常糟糕。

小結(jié)
如果你想要最小化地使用內(nèi)存和并行開銷颜价,請選Serial GC涯保;
如果你想要最大化應(yīng)用程序的吞吐量,請選Parallel GC周伦;
如果你想要最小化GC的中斷或停頓時間夕春,請選CMS GC。

JDK 后續(xù)版本中CMS的變化

  • JDK9新特性: CMS被標(biāo)記為Deprecate了(JEP291)
    • 如果對JDK 9及以上版本的HotSpot虛擬機(jī)使用參數(shù)-XX:+UseConcMarkSweepGC來開啟CMS收集器的話专挪,用戶會收到一個警告信息及志,提示CMS未來將會被廢棄。
  • JDK14新特性: 刪除CMS垃圾回收器(JEP363)
    • 移除了CMS垃圾收集器狈蚤,如果在JDK14中使用-XX: +UseConcMarkSweepGC的話困肩,JVM不會報錯,只是給出一個warning信息脆侮,但是不會exit。JVM會自動回退以默認(rèn)GC方式啟動JVM

8 G1回收器:區(qū)域化分代式


  • G1 (Garbage-First) 是一款面向服務(wù)端應(yīng)用的垃圾收集器勇劣,主要針對配備多核CPU及大容量內(nèi)存的機(jī)器靖避,以極高概率滿足GC停頓時間的同時潭枣,還兼具高吞吐量的性能特征。
  • 在JDK1. 7版本正式啟用幻捏,移除了Experimental的標(biāo)識盆犁,是JDK 9以后的默認(rèn)垃圾回收器,取代了CMS回收器及Parallel + Parallel Old組合篡九。被Oracle官方稱為“全功能的垃圾收集器” 谐岁。
  • 與此同時,CMS已經(jīng)在JDK 9中被標(biāo)記為廢棄(deprecated) 榛臼。G1在jdk8中還不是默認(rèn)的垃圾回收器伊佃,需要使用-XX:+UseG1GC來啟用。

8.1 為什么要發(fā)布Garbage First (G1)GC沛善?

??原因就在于應(yīng)用程序所應(yīng)對的業(yè)務(wù)越來越龐大航揉、復(fù)雜,用戶越來越多金刁,沒有GC就不能保證應(yīng)用程序正常進(jìn)行帅涂,而經(jīng)常造成STW的GC又跟不上實際的需求,所以才會不斷地嘗試對GC進(jìn)行優(yōu)化尤蛮。G1(Garbage-First) 垃圾回收器是在Java7 update4之后引入的一個新的垃圾回收器媳友,是當(dāng)今收集器技術(shù)發(fā)展的最前沿成果之一。
??與此同時产捞,為了適應(yīng)現(xiàn)在不斷擴(kuò)大的內(nèi)存和不斷增加的處理器數(shù)量醇锚,進(jìn)一步降低暫停時間(pause time) ,同時兼顧良好的吞吐量轧葛,官方給G1設(shè)定的目標(biāo)是在延遲可控的情況下獲得盡可能高的吞吐量搂抒,所以才擔(dān)當(dāng)起“全功能收集器”的重任與期望

8.2 為什么叫做Garbage First (G1)呢?

  • 因為G1是一個并行回收器尿扯,它把堆內(nèi)存分割為很多不相關(guān)的區(qū)域(Region) (物理上 不連續(xù)的)求晶。使用不同的Region來表示Eden、幸存者0區(qū)衷笋,幸存者1區(qū)芳杏,老年代等。
  • G1 GC有計劃地避免在整個Java 堆中進(jìn)行全區(qū)域的垃圾收集辟宗。G1跟蹤各個Region 里面的垃圾堆積的價值大芯粽浴(回收所獲得的空間大小以及回收所需時間的經(jīng)驗值),在后臺維護(hù)一個優(yōu)先列表泊脐,每次根據(jù)允許的收集時間空幻,優(yōu)先回收價值最大的Region。
  • 由于這種方式的側(cè)重點(diǎn)在于回收垃圾最大量的區(qū)間(Region)容客,所以我們給G1一個名字:垃圾優(yōu)先(Garbage First) 秕铛。

8.3 優(yōu)勢(特點(diǎn))

與其他GC收集器相比约郁,G1使用了全新的分區(qū)算法,其特點(diǎn)如下所示:

  • 并行與并發(fā)

    • 并行性: G1在回收期間但两,可以有多個GC線程同時工作鬓梅,有效利用多核計算能力。此時用戶線程STW
    • 并發(fā)性: G1擁有與應(yīng)用程序交替執(zhí)行的能力谨湘,部分工作可以和應(yīng)用程序同時執(zhí)行绽快,因此,一般來說紧阔,不會在整個回收階段發(fā)生完全阻塞應(yīng)用程序的情況
  • 分代收集

    • 從分代上看坊罢,G1依然屬于分代型垃圾回收器,它會區(qū)分年輕代和老年代寓辱,年輕代依然有Eden區(qū)和Survivor區(qū)艘绍。但從堆的結(jié)構(gòu)上看,它不要求整個Eden區(qū)秫筏、年輕代或者老年代都是連續(xù)的诱鞠,也不再堅持固定大小和固定數(shù)量。


      不要求堆結(jié)構(gòu)連續(xù)
    • 將堆空間分為若干個區(qū)域(Region) 这敬,這些區(qū)域中包含了邏輯上的年輕代和老年代航夺。

    • 和之前的各類回收器不同,它同時兼顧年輕代和老年代崔涂。對比其他回收器阳掐,或者工作在年輕代,或者工作在老年代冷蚂;


  • 空間整合

    • G1將內(nèi)存劃分為一個個的region缭保。 內(nèi)存的回收是以region作為基本單的。Region之間是復(fù)制算法蝙茶,但整體上實際可看作是標(biāo)記-壓縮(Mark-Compact)算法艺骂,兩種算法都可以避免內(nèi)存碎片。這種特性有利于程序長時間運(yùn)行隆夯,分配大對象時不會因為無法找到連續(xù)內(nèi)存空間而提前觸發(fā)下一次GC钳恕。尤其是當(dāng)Java堆非常大的時候,G1的優(yōu)勢更加明顯蹄衷。
  • 可預(yù)測的停頓時間模型(即:軟實時soft real-time)

    • 這是G1相對于CMS的另一大優(yōu)勢忧额,G1除了追求低停頓外,還能建立可預(yù)測的停頓時間模型愧口,能讓使用者明確指定在一個長度為M毫秒的時間片段內(nèi)睦番,消耗在垃圾收集上的時間不得超過N毫秒。
    • 由于分區(qū)的原因耍属,G1可以只選取部分區(qū)域進(jìn)行內(nèi)存回收抡砂,這樣縮小了回收的范圍大咱,因此對于全局停頓情況的發(fā)生也能得到較好的控制恬涧。
    • G1跟蹤各個Region里面的垃圾堆積的價值大凶⒁妗(回收所獲得的空間大小以 及回收所需時間的經(jīng)驗值),在后臺維護(hù)一個優(yōu)先列表溯捆,每次根據(jù)允許的收集時間丑搔,優(yōu)先回收價值最大的Region。保證了G1 收集器在有限的時間內(nèi)可以獲取盡可能高的收集效率提揍。
    • 相比于CMS GC啤月,G1未必能做到CMS在最好情況下的延時停頓,但是最差情況要好很多劳跃。
  • HotSpot垃圾收集器里谎仲,除了G1以外,其他的垃圾收集器使用內(nèi)置的JVM線程執(zhí)行 GC的多線程操作刨仑,而G1 GC可以采用應(yīng)用線程承擔(dān)后臺運(yùn)行的GC工作郑诺,即當(dāng)JVM的GC線程處理速度慢時,系統(tǒng)會調(diào)用應(yīng)用程序線程幫助加速垃圾回收過程杉武。

8.4 缺點(diǎn)

  • 相較于CMS辙诞,G1還不具備全方位、壓倒性優(yōu)勢轻抱。比如在用戶程序運(yùn)行過程中飞涂,G1無論是為了垃圾收集產(chǎn)生的內(nèi)存占用(Footprint) 還是程序運(yùn)行時的額外執(zhí)行負(fù)載(overload) 都要比CMS要高。
  • 從經(jīng)驗上來說祈搜,在小內(nèi)存應(yīng)用上CMS的表現(xiàn)大概率會優(yōu)于G1较店,而G1在大內(nèi)存應(yīng)用上則發(fā)揮其優(yōu)勢。平衡點(diǎn)在6~8GB之間容燕。

8.5 參數(shù)設(shè)置

  • -XX:+UseG1GC 手動指定使用G1收集器執(zhí)行內(nèi)存回收任務(wù)梁呈。
  • -XX:G1HeapRegionSize 設(shè)置每個Region的大小。值是2的冪缰趋,范圍是1MB 到32MB之間捧杉,目標(biāo)是根據(jù)最小的Java堆大小劃分出約2048個區(qū)域。默認(rèn)是堆內(nèi)存的1/2000秘血。
  • -XX:MaxGCPauseMillis 設(shè)置期望達(dá)到的最大Gc停頓時間指標(biāo)(JVM會盡力實現(xiàn)味抖,但不保證達(dá)到)。默認(rèn)值是200ms
  • -XX:ParallelGCThread 設(shè)置STW工作線程數(shù)的值灰粮。最多設(shè)置為8
  • -XX:ConcGCThreads 設(shè)置并發(fā)標(biāo)記的線程數(shù)仔涩。將n設(shè)置為并行垃圾回收線程數(shù)(ParallelGCThreads)的1/4左右。
  • -XX:InitiatingHeapOccupancyPercent 設(shè)置觸發(fā)并發(fā)GC周期的Java堆占用率閾值粘舟。超過此值熔脂,就觸發(fā)GC佩研。默認(rèn)值是45。

8.6 G1回收器的常見操作步驟

G1的設(shè)計原則就是簡化JVM性能調(diào)優(yōu)霞揉,開發(fā)人員只需要簡單的三步即可完成調(diào)優(yōu):

  • 第一步:開啟G1垃圾收集器
  • 第二步:設(shè)置堆的最大內(nèi)存
  • 第三步:設(shè)置最大的停頓時間

G1中提供了三種垃圾回收模式: YoungGC旬薯、 Mixed GC和Full GC, 在不同的條件下被觸發(fā)适秩。

8.7 適用場景

  • 面向服務(wù)端應(yīng)用绊序,針對具有大內(nèi)存、多處理器的機(jī)器秽荞。(在普通大小的堆里表現(xiàn)并不驚喜)
  • 最主要的應(yīng)用是需要低GC延遲骤公,并具有大堆的應(yīng)用程序提供解決方案;
  • 如:在堆大小約6GB或更大時扬跋,可預(yù)測的暫停時間可以低于0.5秒阶捆; ( G1通過每次只清理一部分而不是全部的Region的增量式清理來保證每次GC停頓時間不會過長)。
  • 用來替換掉JDK1.5中的CMS收集器钦听; 在下面的情況時洒试,使用G1可能比CMS好:
    ①超過50%的Java堆被活動數(shù)據(jù)占用;
    ②對象分配頻率或年代提升頻率變化很大彪见;
    ③GC停頓時間過長(長于0. 5至1秒)儡司。

8.8 region分區(qū)

??使用G1收集器時,它將整個Java堆劃分成約2048個大小相同的獨(dú)立Region塊余指,每個Region塊大小根據(jù)堆空間的實際大小而定捕犬,整體被控制在1MB到32MB之間,且為2的N次冪酵镜,即1MB碉碉, 2MB, 4MB淮韭, 8MB垢粮, 1 6MB, 32MB靠粪±桑可以通過 -XX:G1HeapRegionSize設(shè)定。所有的Region大小相同占键,且在JVM生命周期內(nèi)不會被改變昔善。
??雖然還保留有新生代和老年代的概念,但新生代和老年代不再是物理隔離的了畔乙,它們都是一部分Region (不需要連續(xù))的集合君仆。通過Region的動態(tài)分配方式實現(xiàn)邏輯上的連續(xù)。


  • 一個region 有可能屬于Eden, Survivor 或者Old/Tenured 內(nèi)存區(qū)域返咱。但是一個region只可能屬于一個角色钥庇。圖中的E表示該region屬于Eden內(nèi)存區(qū)域,s表示屬于Survivor內(nèi)存區(qū)域咖摹,O表示屬于Old內(nèi)存區(qū)域评姨。圖中空白的表示未使用的內(nèi)存空間。
  • G1垃圾收集器還增加了一種新的內(nèi)存區(qū)域楞艾,叫做Humongous內(nèi)存區(qū)域参咙,如圖中的H塊。主要用于存儲大對象硫眯,如果超過1. 5個region,就放到H两入。
  • 設(shè)置H的原因:
    • 對于堆中的大對象,默認(rèn)直接會被分配到老年代敲才,但是如果它是一個短期存在的大對象裹纳,就會對垃圾收集器造成負(fù)面影響。為了解決這個問題紧武,G1劃分了一個Humongous區(qū)剃氧,它用來專門存放大對象。如果一個H區(qū)裝不下一個大對象阻星,那么G1會尋找連續(xù)的H區(qū)來存儲朋鞍。為了能找到連續(xù)的H區(qū),有時候不得不啟動Full GC妥箕。G1的大多數(shù)行為都把H區(qū)作為老年代的一部分來看待滥酥。

8.9 G1回收器垃圾回收過程

G1 GC的垃圾回收過程主要包括如下三個環(huán)節(jié):

  • 年輕代GC (Young GC )

  • 老年代并發(fā)標(biāo)記過程( Concurrent Marking)

  • 混合回收(Mixed GC )

  • (如果需要,單線程畦幢、獨(dú)占式坎吻、高強(qiáng)度的Full GC還是繼續(xù)存在的。它針對GC的評估失敗提供了一種失敗保護(hù)機(jī)制宇葱,即強(qiáng)力回收瘦真。)

    順時針, young gc 一> young gc + concurrent mark 一> Mixed GC順序黍瞧,進(jìn)行垃圾回收诸尽。

  • 應(yīng)用程序分配內(nèi)存,當(dāng)年輕代的Eden區(qū)用盡時開始年輕代回收過程雷逆; G1的年輕代收集階段是一個并行的獨(dú)占式收集器弦讽。在年輕代回收期,G1 GC暫停所有應(yīng)用程序線程,啟動多線程執(zhí)行年輕代回收往产。然后從年輕代區(qū)間移動存活對象到Survivor區(qū)間或者老年區(qū)間被碗,也有可能是兩個區(qū)間都會涉及。

  • 當(dāng)堆內(nèi)存使用達(dá)到一定值(默認(rèn)45%)時仿村,開始老年代并發(fā)標(biāo)記過程锐朴。

  • 標(biāo)記完成馬上開始混合回收過程。對于一個混合回收期蔼囊,G1 GC從老年區(qū)間移動存活對象到空閑區(qū)間焚志,這些空閑區(qū)間也就成為了老年代的一部分。和年輕代不同畏鼓,老年代的G1回收器和其他GC不同酱酬,G1的老年代回收器不需要整個老年代被回收,一次只需要掃描/回收一小部分老年代的Region就可以了云矫。同時膳沽,這個老年代Region是和年輕代一起 被回收的。

  • 舉個例子:一個web服務(wù)器让禀,Java進(jìn)程最大堆內(nèi)存為4G挑社,每分鐘響應(yīng)1500個請求,每45秒鐘會新分配大約2G的內(nèi)存巡揍。G1會每45秒鐘進(jìn)行一次年輕代回收痛阻,每31 個小時整個堆的使用率會達(dá)到45%,會開始老年代并發(fā)標(biāo)記過程腮敌,標(biāo)記完成后開始四到五次的混合回收阱当。

8.10 記憶集與寫屏障

  • 一個對象被不同區(qū)域引用的問題(分代引用問題)
  • 一個Region不可能是孤立的,一個Region中的對象可能被其他任意Region中對象引用缀皱,判斷對象存活時斗这,是否需要掃描整個Java堆才能保證準(zhǔn)確?
  • 在其他的分代收集器啤斗,也存在這樣的問題( 而G1更突出)
  • 回收新生代也不得不同時掃描老年代表箭?
  • 這樣的話會降低MinorGC的效率;
  • 解決方法:
    • 無論G1還是其他分代收集器钮莲,JVM都是使用RememberedSet來避免全局掃描:

    • 每個Region都有 一個對應(yīng)的Remembered Set免钻;

    • 每次Reference類型數(shù)據(jù)寫操作時,都會產(chǎn)生一個Write Barrier暫時中斷操作崔拥; .

    • 然后檢查將要寫入的引用指向的對象是否和該Reference類型數(shù)據(jù)在不同的Region (其他收集器:檢查老年代對象是否引用了新生代對象) 极舔;

    • 如果不同,通過CardTable把相關(guān)引用信息記錄到引用指向?qū)ο蟮乃赗egion對應(yīng)的Remembered Set中链瓦;

    • 當(dāng)進(jìn)行垃圾收集時拆魏,在GC根節(jié)點(diǎn)的枚舉范圍加入Remembered Set盯桦;就可以保證不進(jìn)行全局掃描,也不會有遺漏渤刃。

8.11 G1回收過程詳解

1 年輕代GC

  • JVM啟動時拥峦,G1 先準(zhǔn)備好Eden區(qū),程序在運(yùn)行過程中不斷創(chuàng)建對象到Eden區(qū)卖子,當(dāng)Eden空間耗盡時略号,G1會啟動一次年輕代垃圾回收過程。
  • 年輕代垃圾回收只會回收Eden區(qū)和Survivor區(qū)洋闽。
  • YGC時玄柠,首先G1停止應(yīng)用程序的執(zhí)行(Stop-The-World),G1創(chuàng)建回收集(Collection Set)诫舅,回收集是指需要被回收的內(nèi)存分段的集合羽利,年輕代回收過程的回收集包含年輕代Eden區(qū)和Survivor區(qū)所有的內(nèi)存分段。


  • 然后開始如下回收過程:
    • 第一階段骚勘,掃描根铐伴。
      根是指static變量指向的對象,正在執(zhí)行的方法調(diào)用鏈條上的局部變量等俏讹。根引用連同RSet(記憶集)記錄的外部引用作為掃描存活對象的入口。
    • 第二階段畜吊,更新RSet.
      處理dirty card queue中的card泽疆,更新RSet。 此階段完成后玲献,RSet可 以準(zhǔn)確的反映老年代對所在的內(nèi)存分段中對象的引用殉疼。
      • dirty card queue: 對于應(yīng)用程序的引用賦值語句object.field=object,JVM會在之前和之后執(zhí)行特殊的操作以在dirty card queue中入隊一個保存了對象引用信息的card捌年。在年輕代回收的時候瓢娜, G1會對Dirty Card Queue中所有的card進(jìn)行處理,以更新RSet礼预,保證RSet實時準(zhǔn)確的反映引用關(guān)系眠砾。 那為什么不在引用賦值語句處直接更新RSet呢?這是為了性能的需要托酸,RSet的處理需要線程同步褒颈,開銷會很大,使用隊列性能會好很多励堡。
    • 第三階段谷丸,處理RSet
      識別被老年代對象指向的Eden中的對象应结,這些被指向的Eden中的對象被認(rèn)為是存活的對象刨疼。
    • 第四階段,復(fù)制對象
      此階段揩慕,對象樹被遍歷亭畜,Eden區(qū)內(nèi)存段中存活的對象會被復(fù)制到Survivor區(qū)中空的內(nèi)存分段,Survivor區(qū)內(nèi)存段中存活的對象如果年齡未達(dá)閾值漩绵,年齡會加1贱案,達(dá)到閥值會被會被復(fù)制到Old區(qū)中空的內(nèi)存分段。如果Survivor空間不夠止吐,Eden空間的 部分?jǐn)?shù)據(jù)會直接晉升到老年代空間宝踪。
    • 第五階段,處理引用碍扔。
      處理Soft瘩燥,Weak, Phantom不同, Final厉膀, JNI Weak等引用。最終Eden空間的數(shù)據(jù)為空二拐,GC停止工作服鹅,而目標(biāo)內(nèi)存中的對象都是連續(xù)存儲的,沒有碎片百新,所以復(fù)制過程可以達(dá)到內(nèi)存整理的效果企软,減少碎片。

2 并發(fā)標(biāo)記過程

  • 初始標(biāo)記階段:標(biāo)記從根節(jié)點(diǎn)直接可達(dá)的對象饭望。這個階段是STW的仗哨,并且會觸發(fā)一次年輕代GC。
  • 根區(qū)域掃描(Root Region Scanning) : G1 GC掃描Survivor區(qū)直接可達(dá)的老年代區(qū)域?qū)ο笄Υ牵?biāo)記被引用的對象厌漂。這一過程必須在young GC之前完成。
  • 并發(fā)標(biāo)記(Concurrent Marking): 在整個堆中進(jìn)行并發(fā)標(biāo)記(和應(yīng)用程序并發(fā)執(zhí)行)斟珊,此過程可能被young GC中斷苇倡。在并發(fā)標(biāo)記階段,若發(fā)現(xiàn)區(qū)域?qū)ο笾械乃袑ο蠖际抢侗觯沁@個區(qū)域會被立即回收雏节。同時,并發(fā)標(biāo)記過程中高职,會計算每個區(qū)域的對象活性(區(qū)域中存活對象的比例)钩乍。
  • 再次標(biāo)記(Remark): 由 于應(yīng)用程序持續(xù)進(jìn)行,需要修正上一次的標(biāo)記結(jié)果怔锌。是STW的催束。G1中采用了比CMS更快的初始快照算法:snapshot-at-the-beginning (SATB)
  • 獨(dú)占清理(cleanup容贝,STW):計算各個區(qū)域的存活對象和GC回收比例叉抡,并進(jìn)行排序滩报,識別可以混合回收的區(qū)域。為下階段做鋪墊。是STW的。
    • 這個階段并不會實際上去做垃圾的收集
  • 并發(fā)清理階段:識別并清理完全空閑的區(qū)域崭孤。

3 混合回收

當(dāng)越來越多的對象晉升到老年代oldregion時,為了避免堆內(nèi)存被耗盡糊肠,虛擬機(jī)會觸發(fā)一個混合的垃圾收集器辨宠,即Mixed GC, 該算法并不是一個OldGC货裹,除了回收整個Young Region嗤形,還會回收一部分的0ldRegion。這里需要注意:是一部分老年代弧圆, 而不是全部老年代赋兵。可以選擇哪些oldRegion進(jìn)行收集搔预,從而可以對垃圾回收的耗時時間進(jìn)行控制霹期。也要注意的是Mixed GC并不是Full GC。

  • 并發(fā)標(biāo)記結(jié)束以后拯田,老年代中百分百為垃圾的內(nèi)存分段被回收了经伙,部分為垃圾的內(nèi)存分段被計算了出來。默認(rèn)情況下勿锅,這些老年代的內(nèi)存分段會分8次(可以通過一XX: G1MixedGCCountTarget設(shè)置)被回收。
  • 混合回收的回收集(Collection Set) 包括八分之一的老年代內(nèi)存分段枣氧,Eden區(qū)內(nèi)存分段溢十,Survivor區(qū)內(nèi)存分段〈锿蹋混合回收的算法和年輕代回收的算法完全一樣张弛,只是回收集多了老年代的內(nèi)存分段。具體過程請參考上面的年輕代回收過程酪劫。
  • 由于老年代中的內(nèi)存分段默認(rèn)分8次回收吞鸭,G1會優(yōu)先回收垃圾多的內(nèi)存分段。垃圾占內(nèi)存分段比例越高的覆糟,越會被先回收刻剥。并且有一個閾值會決定內(nèi)存分段是否被回收,-XX:G1MixedGCLiveThresholdPercent滩字,默認(rèn)為65%造虏,意思是垃圾占內(nèi)存分段比例要達(dá)到65%才會被回收御吞。如果垃圾占比太低,意味著存活的對象占比高漓藕,在復(fù)制的時候會花費(fèi)更多的時間陶珠。
  • 混合回收并不一定要進(jìn)行8次。有一個閾值-XX: G1HeapWastePercent享钞,默認(rèn)值為10%揍诽,意思是允許整個堆內(nèi)存中有10%的空間被浪費(fèi),意味著如果發(fā)現(xiàn)可以回收的垃圾占堆內(nèi)存的比例低于10%栗竖,則不再進(jìn)行混合回收暑脆。因為GC會花費(fèi)很多的時間但是回收到的內(nèi)存卻很少。

4 Full GC

??G1的初衷就是要避免Full GC的出現(xiàn)划滋。但是如果上述方式不能正常工作饵筑,G1會停止應(yīng)用程序的執(zhí)行(Stop-The-World),使用單線程的內(nèi)存回收算法進(jìn)行垃圾回收处坪,性能會非常差根资,應(yīng)用程序停頓時間會很長。
??要避免Full GC的發(fā)生同窘,一旦發(fā)生需要進(jìn)行調(diào)整玄帕。什么時候會發(fā)生Full GC呢?比如堆內(nèi)存太小想邦,當(dāng)G1在復(fù)制存活對象的時候沒有空的內(nèi)存分段可用裤纹,則會回退到full gc, 這種情況可以通過增大內(nèi)存解決丧没。

導(dǎo)致G1Full GC的原因可能有兩個:

  • 1.Evacuation的時候沒有足夠的to一 space來存放晉升的對象鹰椒;
  • 2.并發(fā)處理過程完成之前空間耗盡。

補(bǔ)充

??從Oracle官方透露出來的信息可獲知呕童,回收階段(Evacuation)其實.本也有想過設(shè)計成與用戶程序一起并發(fā)執(zhí)行漆际,但這件事情做起來比較復(fù)雜,考慮到G1只是回收一部分Region夺饲, 停頓時間是用戶可控制的奸汇,所以并不迫切去實現(xiàn),而選擇把這個特性放到了G1之后出現(xiàn)的低延遲垃圾收集器(即ZGC)中往声。另外擂找,還考慮到G1不是僅僅面向低延遲,停頓用戶線程能夠最大幅度提高垃圾收集效率浩销,為了保證吞吐量所以才選擇了完全暫停用戶線程的實現(xiàn)方案贯涎。

8.12 優(yōu)化建議

  • 年輕代大小
    • 避免使用-Xmn或-XX:NewRatio等相關(guān)選項顯式設(shè)置年輕代大小。固定年輕代的大小會覆蓋暫停時間目標(biāo)
  • 暫停時間目標(biāo)不要太過嚴(yán)苛
    • G1 GC的吞吐量目標(biāo)是90%的應(yīng)用程序時間和10%的垃圾回收時間
    • 評估G1 GC的吞吐量時撼嗓,暫停時間目標(biāo)不要太嚴(yán)苛柬采。目標(biāo)太過嚴(yán)苛表示你愿意承受更多的垃圾回收開銷欢唾,而這些會直接影響到吞吐量。

9 垃圾回收器總結(jié)


截止JDK 1.8粉捻,一共有7款不同的垃圾收集器礁遣。每一款不同的垃圾收集器都有不同的特點(diǎn),在具體使用的時候肩刃,需要根據(jù)具體的情況選用不同的垃圾收集器祟霍。


不同廠商、不同版本的虛擬機(jī)實現(xiàn)差別很大盈包。HotSpot 虛擬機(jī)在JDK7/8后所有收集器及組合(連線)沸呐,如下圖:
[圖片上傳中...(image-4da874-1596784089014-19)]

GC發(fā)展階段: Serial => Parallel (并行) => CMS (并發(fā)) => G1 => ZGC

怎么選擇垃圾回收器

  • Java垃圾收集器的配置對于JVM優(yōu)化來說是一個很重要的選擇,選擇合適的垃圾收集器可以讓JVM的性能有一個很大的提升呢燥。

  • 怎么選擇垃圾收集器崭添?

    • 1.優(yōu)先調(diào)整堆的大小讓JVM自適應(yīng)完成。
    • 2.如果內(nèi)存小于100M叛氨,使用串行收集器
    • 3.如果是單核呼渣、單機(jī)程序,并且沒有停頓時間的要求寞埠,串行收集器
    • 4.如果是多CPU屁置、需要高吞吐量、允許停頓時間超過1秒仁连,選擇并行或者JVM自己選擇
    • 5.如果是多CPU蓝角、追求低停頓時間,需快速響應(yīng)(比如延遲不能超過1秒饭冬,如互聯(lián)網(wǎng)應(yīng)用)使鹅,使用并發(fā)收集器
  • 最后需要明確一一個觀點(diǎn):

    • 1.沒有最好的收集器,更沒有萬能的收集昌抠;
    • 2.調(diào)優(yōu)永遠(yuǎn)是針對特定場景并徘、特定需求,不存在一勞永逸的收集器

10 GC日志分析


通過閱讀GC日志扰魂,我們可以了解Java虛擬機(jī)內(nèi)存分配與回收策略。內(nèi)存分配與垃圾回收的參數(shù)列表

  • -XX:+PrintGC 輸出GC日志蕴茴。類似: -verbose:gc
  • -XX: +PrintGCDetails 輸出GC的詳細(xì)日志
  • -XX: +PrintGCTimeStamps 輸出GC的時間戳(以基準(zhǔn)時間的形式)
  • -XX: +PrintGCDateStamps輸出GC的時間戳(以日期的形式劝评,如2013-05-04T21 : 53:59.234+0800 )
  • -XX: +PrintHeapAtGC 在進(jìn)行GC的前后打印出堆的信息
  • -Xloggc: . . /logs/gc. log日志文件的輸出路徑
+PrintGC
  • 打開GC日志:-verbose:gc / -XX:+PrintGC
  • 這個只會顯示總的GC堆的變化, 如下:
[GC (Allocation Failure) 80832K一>19298K(227840K)倦淀,0.0084018 secs]
[GC (Metadata GC Threshold) 109499K一>21465K (228352K)蒋畜,0.0184066 secs]
[Full GC (Metadata GC Threshold) 21 465K一>16716K (201728K),0.0619261 secs ]
  • 參數(shù)解析:
GC撞叽、Full GC: GC的類型姻成,GC只在新生代上進(jìn)行插龄,F(xiàn)ull GC包括永生代,新生代科展, 老年代均牢。
Allocation Failure: GC發(fā)生的原因。
80832K一> 19298K:堆在GC前的大小和GC后的大小才睹。
228840k:現(xiàn)在的堆大小徘跪。
0.0084018 secs: GC持續(xù)的時間。
PrintGCDetails
  • 打開GC日志: -XX:+PrintGCDetaiis

  • 輸入信息如下:

[GC (Allocation Failure) [ PSYoungGen: 70640K一> 10116K(141312K) ] 80541K一>20017K (227328K)琅攘,0.0172573 secs] [Times: user=0.03 sys=0.00垮庐, real=0.02 secs ]
[GC (Metadata GC Threshold) [PSYoungGen:98859K一>8154K(142336K) ] 108760K一>21261K (228352K),
0.0151573 secs] [Times: user=0.00 sys=0.01坞琴, real=0.02 secs]
[Full GC (Metadata GC Threshold) [PSYoungGen: 8154K一>0K(142336K) ] [ParOldGen: 13107K一>16809K(62464K) ] 21261K一>16809K (204800K)哨查,[Metaspace: 20599K一>20599K (1067008K) ],0.0639732 secs]
[Times: user=0.14 sys=0.00剧辐, real=0.06 secs]
  • 參數(shù)解析:
GC寒亥,F(xiàn)ull FC:同樣是GC的類型
Allocation Failure: GC原因
PSYoungGen:使用了Parallel Scavenge并行垃圾收集器的新生代GC前后大小的變化
ParOldGen:使用了Parallel Old并行垃圾收集器的老年代GC前后大小的變化
Metaspace: 元數(shù)據(jù)區(qū)GC前后大小的變化,JDK1.8中引入了 元數(shù)據(jù)區(qū)以替代永久代
xxx secs : 指Gc花費(fèi)的時間
Times: user: 指的是垃圾收集器花費(fèi)的所有CPU時間浙于,sys: 花費(fèi)在等待系統(tǒng)調(diào)用或系統(tǒng)事件的時間护盈, real :GC從開始到結(jié)束的時間,包括其他進(jìn)程占用時間片的實際時間羞酗。

PrintGCTimeStamps

  • 打開GC日志: -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps
  • 輸入信息如下:
2019一09一24T22:15:24.518+0800:3.287: [GC(Allocation Failure) [ PSYoungGen: 1361 62K一>5113K(136192K) ] 141425K一>17632K (222208K) 腐宋,0.0248249 secs] [Times: user=0.05sys=0.00, real=0.03 secs ]
2019一09一24T22:15:25.559+0800:4.329: [ GC(Metadata GC Threshold)[PSYoungGen:97578K一>10068K(274944K) ] 110096K一>22658K (360960K)檀轨,0.0094071 secs]
[Times: user=0\. 00sys=0.00胸竞, real=0\. 01 secs]
2019一09一24T22:15:25.569+0800:4.338: [Full GC (Metadata GC Threshold)[ PSYoungGen:10068K一>0K(274944K) ] [ ParoldGen: 12590K一>13564K (56320K) ] 22658K一>13564K (331264K) ,
[Metaspace: 20590K一>20590K(1067008K)]参萄, 0\. 0494875 secs]
[Times: user=0.17 sys=0\. 02卫枝,real=0.05 secs ]     

說明:帶上了日期和時間

補(bǔ)充說明
  • "[GC"和"[Full GC"說明了這次垃圾收集的停頓類型,如果有"Full"則說明GC發(fā)生了"StopThe World"
  • 使用Serial收集器在新生代的名字是Default New Generation讹挎, 因此顯示的是" [DefNew"
  • 使用ParNew收集器在新生代的名字會變成" [ParNew"校赤,意思是"Parallel New Generation"
  • 使用Parallel Scavenge收 集器在新生代的名字是"[PSYoungGen"
  • 老年代的收集和新生代道理一樣,名字也是收集器決定的
  • 使用G1收集器的話筒溃,會顯示為"garbage-first heap"
  • Allocation Failure 表明本次引起GC的原因是因為在年輕代中沒有足夠的空間能夠存儲新的數(shù)據(jù)了马篮。
  • [PSYoungGen: 5986K一>696K(8704K)] 5986K一> 704K (9216K)
    • 中括號內(nèi): GC回收前年輕代占用大小,回收后占用大小怜奖,( 年輕代總大谢氩狻)
    • 括號外: GC回收前年輕代和老年代占用大小,回收后占用大小歪玲,( 年輕代和老年代總大星ㄑ搿)
  • user代表用戶態(tài)回收耗時掷匠,sys 內(nèi)核態(tài)回收耗時, rea實際耗時岖圈。由于多核的原因讹语,時間總和可能會超過real時間
堆空間日志分析

Minor GC
Minor GC日志分析

Full GC
Full GC日志分析
/**
 * 在jdk7 和 jdk8中分別執(zhí)行
 * -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC
 */
public class GCLogTest1 {
    private static final int _1MB = 1024 * 1024;

    public static void testAllocation() {
        byte[] allocation1, allocation2, allocation3, allocation4;
        allocation1 = new byte[2 * _1MB];
        allocation2 = new byte[2 * _1MB];
        allocation3 = new byte[2 * _1MB];
        allocation4 = new byte[4 * _1MB];
    }

    public static void main(String[] agrs) {
        testAllocation();
    }
}

GC前

GC后
日志分析工具使用

可以用一些工具去分析這些gc日志。
常用的日志分析工具有: GCViewer幅狮、GCEasy募强、GCHisto、GCLogViewer 崇摄、Hpjmeter擎值、garbagecat等。

11 垃圾回收器的新發(fā)展

??GC仍然處于飛速發(fā)展之中逐抑,目前的默認(rèn)選項G1 GC在不斷的進(jìn)行改進(jìn)鸠儿,很多我們原來認(rèn)為的缺點(diǎn),例如串行的Full GC厕氨、Card Table掃描的低效等进每,都已經(jīng)被大幅改進(jìn),例如命斧,JDK 10以后田晚,F(xiàn)u1l GC已經(jīng)是并行運(yùn)行,在很多場景下国葬,其表現(xiàn)還略優(yōu)于Parallel GC的并行Full GC實現(xiàn)贤徒。
??即使是Serial GC,雖然比較古老汇四,但是簡單的設(shè)計和實現(xiàn)未必就是過時的接奈,它本身的開銷,不管是GC相關(guān)數(shù)據(jù)結(jié)構(gòu)的開銷通孽,還是線程的開銷序宦,都是非常小的,所以隨著云計算的興起背苦,在Serverless等新的應(yīng)用場景下互捌,Serial GC找到了新的舞臺。
??比較不幸的是CMS GC行剂, 因為其算法的理論缺陷等原因疫剃,雖然現(xiàn)在還有非常大的用戶群體,但在JDK9中已經(jīng)被標(biāo)記為廢棄硼讽,并在JDK14版本中移除。

JDK11 新特性

  • JEP318 : Epsilon: A No-Op Garbage Collector (Epsilon 垃圾回收器,"No-Op (無操作) "回收器) http: / /openidk.java.net/ieps/318
  • JEP333: ZGC: A Scalable Low一 Latency ;Garbage Collector (Experimental) ( ZGC:可伸縮的低延遲垃圾回收器牲阁,處于試驗性階段)

Open JDK12 的Shenandoah GC:低停頓時間的GC (實驗性)

  • 現(xiàn)在G1回收器已成為默認(rèn)回收器好幾年了固阁。

  • 我們還看到了引入了兩個新的收集器: ZGC ( JDK11出現(xiàn))和Shenandoah(Open JDK12) 壤躲。

    • 主打特點(diǎn):低停頓時間
  • Shenandoah,無疑是眾多GC中最孤獨(dú)的一個备燃。是第一款不由Oracle公司團(tuán)隊領(lǐng)導(dǎo)開發(fā)的HotSpot垃圾收集器碉克。不可避免的受到官方的排擠。比如號稱OpenJDK和OracleJDK沒有區(qū)別的Oracle公司仍拒絕在OracleJDK12中支持Shenandoah并齐。

  • Shenandoah垃圾回收器最初由RedHat進(jìn)行的一項垃 圾收集器研究項目PauselessGC的實現(xiàn)漏麦,旨在針對JVM上的內(nèi)存回收實現(xiàn)低停頓的需求。在2014年貢獻(xiàn)給OpenJDK况褪。

  • Red Hat研發(fā)Shenandoah團(tuán)隊對外宣稱撕贞,Shenandoah垃圾回收器的暫停時間與堆大小無關(guān),這意味著無論將堆設(shè)置為200MB還是200GB测垛,99.9%的目標(biāo)都可以把垃圾收集的停頓時間限制在十毫秒以內(nèi)捏膨。不過實際使用性能將取決于實際工作堆的大小和工作負(fù)載。


  • 這是RedHat在2016年發(fā)表的論文數(shù)據(jù)食侮,測試內(nèi)容是使用Es對200GB的維基百科數(shù)據(jù)進(jìn)行索引号涯。從結(jié)果看:

    • 停頓時間比其他幾款收集器確實有了質(zhì)的飛躍,但也未實現(xiàn)最大停頓時間控制在十毫秒以內(nèi)的目標(biāo)锯七。
    • 而吞吐量方面出現(xiàn)了明顯的下降链快,總運(yùn)行時間是所有測試收集器里最長的。
  • Shenandoah GC的弱項:高運(yùn)行負(fù)擔(dān)下的吞吐量下降眉尸。

  • Shenandoah GC的強(qiáng)項:低延遲時間域蜗。

革命性的ZGC

??ZGC官網(wǎng)鏈接與Shenandoah目標(biāo)高度相似,在盡可能對吞吐量影響不大的前提下效五,實現(xiàn)在任意堆內(nèi)存大小下都可以把垃圾收集的停頓時間限制在十毫秒以內(nèi)的低延遲地消。
??《深入理解Java虛擬機(jī)》一書中這樣定義ZGC: ZGC收集器是一款基于Region內(nèi)存布局的,(暫時) 不設(shè)分代的畏妖,使用了讀屏障脉执、染色指針和內(nèi)存多重映射等技術(shù)來實現(xiàn)可并發(fā)的標(biāo)記-壓縮算法的,以低延遲為首要目標(biāo)的一款垃圾收集器戒劫。
??ZGC的工作過程可以分為4個階段:并發(fā)標(biāo)記一并發(fā)預(yù)備重分配一并發(fā)重分配一并發(fā)重映射等半夷。
??ZGC幾乎在所有地方并發(fā)執(zhí)行的,除了初始標(biāo)記的是STW的迅细。所以停頓時間幾乎就耗費(fèi)在初始標(biāo)記上巫橄,這部分的實際時間是非常少的。

測試數(shù)據(jù)如圖:

  • 劣勢比較


  • 優(yōu)勢比較


在ZGC的強(qiáng)項停頓時間測試上茵典,它毫不留情的將Parallel湘换、G1拉開了兩個數(shù)量級的差距。無論平均停頓、958停頓彩倚、998停頓筹我、99. 98停頓,還是最大停頓時間帆离,ZGC 都能毫不費(fèi)勁控制在10毫秒以內(nèi)蔬蕊。

JDK14新特性

  • JEP 364: ZGC應(yīng)用在macOS上

  • JEP 365: ZGC應(yīng)用在windows上** JDK14之前,ZGC僅Linux才支持

  • 盡管許多使用ZGC的用戶都使用類Linux的環(huán)境哥谷,但在Windows和macOS 上岸夯,人們也需要ZGC進(jìn)行開發(fā)部署和測試。許多桌面應(yīng)用也可以從ZGC中受益们妥。因此猜扮,ZGC特性被移植到了Windows和macOs.上。

  • 現(xiàn)在mac或Windows 上也能使用zGC了王悍,示例如下: -XX:+Unloc kExperimentalVMOptions -XX: +UseZGC .

其他垃圾回收器:AliGC

AliGC是阿里巴巴JVM團(tuán)隊基于G1算法破镰,面 向大堆(LargeHeap)應(yīng)用場景。指定場景下的對比:


當(dāng)然压储,其他廠商也提供了各種獨(dú)具一格的GC實現(xiàn)鲜漩,例如比較有名的低延遲GC,Zing
https://www.infoq.com/articles/azul_gc_in_detail/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末集惋,一起剝皮案震驚了整個濱河市孕似,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌刮刑,老刑警劉巖喉祭,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異雷绢,居然都是意外死亡泛烙,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門翘紊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蔽氨,“玉大人,你說我怎么就攤上這事帆疟○木浚” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵踪宠,是天一觀的道長自赔。 經(jīng)常有香客問我,道長柳琢,這世上最難降的妖魔是什么绍妨? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任润脸,我火速辦了婚禮,結(jié)果婚禮上他去,老公的妹妹穿的比我還像新娘津函。我一直安慰自己,他們只是感情好孤页,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著涩馆,像睡著了一般行施。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上魂那,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天蛾号,我揣著相機(jī)與錄音,去河邊找鬼涯雅。 笑死鲜结,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的活逆。 我是一名探鬼主播精刷,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蔗候!你這毒婦竟也來了怒允?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤锈遥,失蹤者是張志新(化名)和其女友劉穎纫事,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體所灸,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡丽惶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了爬立。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钾唬。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖懦尝,靈堂內(nèi)的尸體忽然破棺而出知纷,到底是詐尸還是另有隱情,我是刑警寧澤陵霉,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布琅轧,位于F島的核電站,受9級特大地震影響踊挠,放射性物質(zhì)發(fā)生泄漏乍桂。R本人自食惡果不足惜冲杀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望睹酌。 院中可真熱鬧权谁,春花似錦、人聲如沸憋沿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽辐啄。三九已至采章,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間壶辜,已是汗流浹背悯舟。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留砸民,地道東北人抵怎。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像岭参,于是被迫代替她去往敵國和親反惕。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353