溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

java中的串行并行及CMS垃圾回收器是怎樣的

發(fā)布時(shí)間:2021-09-27 09:39:50 來(lái)源:億速云 閱讀:130 作者:柒染 欄目:編程語(yǔ)言

本篇文章給大家分享的是有關(guān)java中的串行并行及CMS垃圾回收器是怎樣的,小編覺(jué)得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說(shuō),跟著小編一起來(lái)看看吧。

1. 串行回收器: SerialGC

串行回收器是指使用單線程的回收器。每次回收器,串行回收器只有一個(gè)工作線程,對(duì)于并行能力較弱的計(jì)算機(jī)來(lái)說(shuō),串行回收器的專注性和獨(dú)占性往往能讓其有更好的性能表現(xiàn)。

串行回收器可以在新生代和老年代使用,根據(jù)不同的堆空間分為新生代串行回收器和老年代串行器。

1.1 新生代串行回收器:SerialGC

串行回收器主要有兩個(gè)特點(diǎn):

  • 僅僅使用單線程進(jìn)行垃圾回收

  • 獨(dú)占式的垃圾回收方式

使用 -XX:+UseSerialGC 參數(shù)可以指定使用新生代串行回收器或老年代串行回收器。當(dāng)虛擬機(jī)在Client模式下運(yùn)行時(shí),它是默認(rèn)的垃圾回收器。

1.2 老年代串行回收器:SerialOldGC

老年代串行回收器使用的標(biāo)記壓縮法,和新生代串行回收器一樣,它也是一個(gè)串行的獨(dú)占式的垃圾回收器。由于老年代垃圾回收通常會(huì)需要比新生代垃圾回收更長(zhǎng)的時(shí)間,在堆空間較大的應(yīng)用程序中,一旦老年代串行回收器啟動(dòng),應(yīng)用程序很可能會(huì)停頓較長(zhǎng)的時(shí)間。

若要開啟老年代串行回收器,可以嘗試使用以下參數(shù)

  • -XX:+UseSerialGC:新生代老年代都使用串行回收器

  • -XX:+UseParNewGc:新生代使用parNew回收器,老年代使用串行回收器,jdk9、jdk10已刪除該參數(shù),因?yàn)镻arNew需要和CMS搭配工作,而CMS已經(jīng)被G1替代,不再支持此參數(shù)。

  • -XX:+UseParallelGC:新生代使用ParallelGC回收器,老年代使用串行回收器。

1.3 回收示例

示例代碼:

public class Demo02 {
    public static void main(String[] args) {
        byte[] b = null;
        for(int i = 0; i < 10; i++) {
            b = new byte[2 * 1024 * 1024];
        }
    }
}

使用參數(shù) -Xmx10m -Xms10m -Xmn2m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC 運(yùn)行,gc 日志如下:

//回收新生代
[GC (Allocation Failure) [DefNew: 1024K->337K(1536K), 0.0007683 secs] 1024K->337K(9728K), 0.0007883 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
//回收新生代與老年代
[GC (Allocation Failure) [DefNew: 645K->37K(1536K), 0.0008160 secs][Tenured: 6470K->2410K(8192K), 0.0015724 secs] 6789K->2410K(9728K), [Metaspace: 2952K->2952K(1056768K)], 0.0024163 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
// 省略其他日志...

日志中的 DefNew 表示新生代,Tenured 表示老年代。

2. 并行回收器

并行回收器在串行回收器的基礎(chǔ)上做了改進(jìn),它使用了多個(gè)線程同時(shí)進(jìn)行垃圾回收。對(duì)于并行能力強(qiáng)的計(jì)算機(jī),可以有效減少垃圾回收所需要的時(shí)間。

2.1 SerialGC的多線程版本:ParNew 回收器

ParNew 回收器是一個(gè)工作在新生代的垃圾回收器。

它只是簡(jiǎn)單地將串行回收器多線程化,它的回收策略、算法及參數(shù)和新生代串行回收器一樣。在并發(fā)能力比較強(qiáng)的cpu上,它產(chǎn)生的停頓時(shí)間要短于串行回收器,而單cpu或者并發(fā)能力較弱的系統(tǒng)中,并行回收器的效果不會(huì)比串行回收器好,由于多線程的壓力,它的實(shí)際表現(xiàn)很可能比串行回收器差。

開啟 ParNew 回收器可以使用以下參數(shù):

  • -XX:+UseParNewGC:新生代使用ParNew回收器,老年代使用串行回收器。jdk9、jdk10中已經(jīng)刪除,因?yàn)镻arNew需要和CMS搭配工作,而CMS已經(jīng)被G1替代,不再支持此參數(shù)。

  • -XX:+UseConcMarkSweepGC:新生代使用ParNew回收器,老年代使用CMS.jdk9、jdk10中不建議使用,建議使用默認(rèn)的G1垃圾回收器。

ParNew回收器工作時(shí)的線程數(shù)可以使用-XX:ParallelGCThreads參數(shù)指定。一般,最好與CPU數(shù)量相當(dāng),避免過(guò)多的線程數(shù)影響垃圾回收性能。在默認(rèn)情況下,當(dāng)CPU數(shù)量小于8時(shí),ParallelGCThreads的值等于CPU數(shù)量,當(dāng)CPU數(shù)量大于8時(shí),ParallelGCThreads的值等于3+((5xCPU_Count)/8)(CPU數(shù)量為16時(shí),ParallelGCThreads的值為13)

示例代碼:

public class Demo02 {
    public static void main(String[] args) {
        byte[] b = null;
        for(int i = 0; i < 10; i++) {
            b = new byte[2 * 1024 * 1024];
        }
    }
}

使用參數(shù) -Xmx10m -Xms10m -Xmn2m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseParNewGC 運(yùn)行,gc 日志如下:

[GC (Allocation Failure) [ParNew: 1024K->384K(1536K), 0.0004642 secs] 1024K->384K(9728K), 0.0004796 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [ParNew: 691K->265K(1536K), 0.0009477 secs][Tenured: 6509K->2407K(8192K), 0.0012491 secs] 6835K->2407K(9728K), [Metaspace: 2933K->2933K(1056768K)], 0.0022175 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [ParNew: 41K->45K(1536K), 0.0006914 secs][Tenured: 6503K->2407K(8192K), 0.0012305 secs] 6544K->2407K(9728K), [Metaspace: 2936K->2936K(1056768K)], 0.0019425 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
// 省略其他日志...

可以看到,這個(gè)輸出和新生代串行回收器的輸出幾乎一致,只有回收器標(biāo)識(shí)符不同。

2.2 關(guān)注吞吐量的回收器:ParallelGC

ParallelGC是一個(gè)既可以工作在新生代,又可以工作在老年代的垃圾回收器。工作在新生代時(shí),叫ParallelGC,工作在老年代時(shí)叫ParallelOldGC

2.2.1 新生代 ParallelGC 回收器

新生代 ParallelGC 回收器也是使用復(fù)制算法的回收器。從表面上看,它和ParaNew回收器一樣,都是多線程、獨(dú)占式的回收器,但是Parallel回收器有一個(gè)重要的特點(diǎn):它非常關(guān)注系統(tǒng)的吞吐量。

新生代Parallel回收器可以使用以下參數(shù)啟用:

  • -XX:+UseParallelGC:新生代使用ParallelGC回收器,老年代使用串行回收器。

  • -XX:+UseParallelOldGC:新生代使用ParallelGC回收器,老年代使用ParallelOldGC回收器。

ParallelGC回收器提供了兩個(gè)重要的參數(shù)用于控制系統(tǒng)的吞吐量:

  • -XX:MaxGCPauseMillis:設(shè)置最大垃圾回收停頓時(shí)間。它的值是一個(gè)大于0的整數(shù)。ParallelGC 在工作時(shí),會(huì)調(diào)整java堆大小或者其他參數(shù),盡可能指導(dǎo)停頓時(shí)間控制在MaxGCPauseMillis以內(nèi)。

  • -XX:GCTimeRatio:設(shè)置吞吐量大小。它的值是一個(gè)0到100之間的整數(shù),假設(shè)GCTimeRation的值為n,那么系統(tǒng)將花費(fèi)不超過(guò)1/(1+n)的時(shí)間進(jìn)行垃圾回收,默認(rèn)情況下它的取值是99,即有不超過(guò)1/(1+99)=1%的時(shí)間用于垃圾回收。

除此之外,ParallelGC回收器還支持一種自適應(yīng)的GC調(diào)節(jié)策略,使用-XX:+UseAdaptiveSizePolicy可以打開自適應(yīng)策略。在這種模式下,新生代的大小、eden區(qū)和survivor區(qū)的比例、晉升老年代的對(duì)象年齡等參數(shù)會(huì)被自動(dòng)調(diào)整,以達(dá)到堆大小、吞吐量和停頓時(shí)間之間的平衡點(diǎn)。

2.2.2 老年代 ParallelOldGC 回收器

老年代ParallelOldGC回收器也是一種多線程并發(fā)的回收器。和新生代ParallelGC回收器一樣,它也是一種關(guān)注吞吐量的回收器。

老年代ParallelOldGC可以使用以下參數(shù)啟用:

  • -XX:+UseParallelOldGC:新生代使用ParallelGC回收器,老年代使用ParallelOldGC回收器。

參數(shù) -XX:ParallelGCThreads 也可以用于設(shè)置垃圾回收時(shí)的線程數(shù)量。

2.2.3 回收示例

示例代碼:

public class Demo02 {
    public static void main(String[] args) {
        byte[] b = null;
        for(int i = 0; i < 10; i++) {
            b = new byte[2 * 1024 * 1024];
        }
    }
}

使用參數(shù) -Xmx10m -Xms10m -Xmn2m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseParallelOldGC 運(yùn)行,gc 日志如下:

//回收年輕代
[GC (Allocation Failure) [PSYoungGen: 464K->432K(1536K)] 6616K->6584K(9728K), 0.0013387 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
//回收老年代
[Full GC (Allocation Failure) [PSYoungGen: 432K->0K(1536K)] [ParOldGen: 6152K->2407K(8192K)] 6584K->2407K(9728K), [Metaspace: 2928K->2928K(1056768K)], 0.0032880 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
2.3 CMS回收器(jdk8及之前的版本)

ParallelGCParallelOldGC 不同,CMS (Concurrent Mark Sweep,意為并發(fā)標(biāo)記清除) 回收器主要關(guān)注系統(tǒng)停頓時(shí)間。它是一個(gè)老年代垃圾回收器。

CMS主要工作步驟如下:

  1. 初始標(biāo)記(CMS-initial-mark):標(biāo)記根對(duì)象(STW)

  2. 并發(fā)標(biāo)記(CMS-concurrent-mark):標(biāo)記所有對(duì)象

  3. 預(yù)清理(MS-concurrent-preclean):清理前準(zhǔn)備及控制停頓時(shí)間

  4. 重新標(biāo)記(CMS-remark):修正并發(fā)標(biāo)記數(shù)據(jù)(STW)

  5. 并發(fā)清理(CMS-concurrent-sweep):清理垃圾

  6. 并發(fā)重置(CMS-concurrent-reset):垃圾回收完成后,重新初始化CMS數(shù)據(jù),為下一次垃圾回收做準(zhǔn)備。

以上過(guò)程中,并發(fā)標(biāo)記并發(fā)清理并發(fā)重置都是可以和應(yīng)用線程一起執(zhí)行的。

CMS 回收器的主要參數(shù)如下:

  • 啟動(dòng)CMS垃圾回收器:-XX:+UseConcMarkSweepGC

  • 關(guān)閉預(yù)清理:-XX:-CMSPrecleaningEnabled。在整個(gè)CMS的回收過(guò)程中,默認(rèn)情況下,在并發(fā)標(biāo)記之后,會(huì)有一個(gè)預(yù)清理的操作,預(yù)清理是并發(fā)的,除了為正式清理做準(zhǔn)備檢查,還會(huì)嘗試控制一次停頓時(shí)間。

  • 設(shè)置CMS的并發(fā)線程數(shù):CMS默認(rèn)啟動(dòng)的回收線程數(shù)目是 (ParallelGCThreads + 3)/4) ,如果你需要明確設(shè)定,可以通過(guò)-XX:ParallelCMSThreads=20來(lái)設(shè)定,其中ParallelGCThreads是年輕代的并行收集線程數(shù).

  • 指定內(nèi)存回收的閾值:由于CMS不是獨(dú)占式的,在回收過(guò)程中,應(yīng)用程序仍然在不停地工作。在應(yīng)用程序工作的過(guò)程中,又會(huì)不斷地產(chǎn)生垃圾,這些新垃圾在當(dāng)前的回收過(guò)程中是無(wú)法清除的。同時(shí),因?yàn)閼?yīng)用程序沒(méi)有中斷,所以在CMS回收過(guò)程中,還應(yīng)該確保應(yīng)用程序有足夠的內(nèi)存可用。因此,CMS回收器不會(huì)等到堆內(nèi)存飽和時(shí)才進(jìn)行垃圾回收,而當(dāng)堆內(nèi)存使用率達(dá)到某一閾值時(shí)便開始進(jìn)行回收。這個(gè)回收閾值可以通過(guò)參數(shù)-XX:CMSInitiatingOccupancyFraction指定,默認(rèn)是68,即老年代空間使用率達(dá)到68%時(shí),會(huì)執(zhí)行一次CMS回收。

  • 開啟碎片整理:CMS是不會(huì)整理堆碎片的,因此為了防止堆碎片引起full gc,通過(guò)會(huì)開啟CMS階段進(jìn)行合并碎片選項(xiàng):-XX:+UseCMSCompactAtFullCollection,內(nèi)存碎片的整理不是并發(fā)進(jìn)行的,因此性能上會(huì)有所消耗。

  • 指定多少次CMS回收后,進(jìn)行一次內(nèi)存壓縮:-XX:CMSFullGCBeforeCompaction

  • 回收Perm區(qū):CMS默認(rèn)不回收Perm區(qū)的,如果Perm區(qū)滿了,會(huì)觸發(fā)一次FullGC。如果希望使用CMS回收Perm區(qū),可以使用參數(shù)-XX:+CMSClassUnloadingEnabled。

示例代碼:

public class Demo02 {
    public static void main(String[] args) {
        byte[] b = null;
        for(int i = 0; i < 10; i++) {
            b = new byte[2 * 1024 * 1024];
        }
    }
}

使用參數(shù) -Xmx10m -Xms10m -Xmn2m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseConcMarkSweepGC 運(yùn)行,gc 日志如下:

[GC (Allocation Failure) [ParNew: 1024K->362K(1536K), 0.0013608 secs] 1024K->362K(9728K), 0.0014303 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [ParNew: 730K->264K(1536K), 0.0028882 secs][CMS: 6490K->2418K(8192K), 0.0025366 secs] 6874K->2418K(9728K), [Metaspace: 3008K->3008K(1056768K)], 0.0054949 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
[GC (CMS Initial Mark) [1 CMS-initial-mark: 4466K(8192K)] 4466K(9728K), 0.0004792 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[CMS-concurrent-mark-start]
[GC (Allocation Failure) [ParNew: 41K->55K(1536K), 0.0008476 secs][CMS[CMS-concurrent-mark: 0.002/0.003 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
 (concurrent mode failure): 6514K->2418K(8192K), 0.0035573 secs] 6555K->2418K(9728K), [Metaspace: 3016K->3016K(1056768K)], 0.0044327 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [ParNew: 41K->11K(1536K), 0.0008080 secs][CMS: 6514K->2419K(8192K), 0.0013165 secs] 6555K->2419K(9728K), [Metaspace: 3026K->3026K(1056768K)], 0.0021484 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
[GC (CMS Initial Mark) [1 CMS-initial-mark: 4467K(8192K)] 4467K(9728K), 0.0002437 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[CMS-concurrent-mark-start]
[GC (Allocation Failure) [ParNew: 20K->4K(1536K), 0.0006944 secs][CMS[CMS-concurrent-mark: 0.001/0.002 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
 (concurrent mode failure): 6515K->2419K(8192K), 0.0023357 secs] 6536K->2419K(9728K), [Metaspace: 3037K->3037K(1056768K)], 0.0030568 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]

相關(guān)信息說(shuō)明如下:

  • CMS Initial Mark:初始標(biāo)記

  • CMS-concurrent-mark:并發(fā)標(biāo)記

  • CMS-concurrent-preclean-start:預(yù)清理

  • CMS-concurrent-abortable-preclean: 可中止的預(yù)清理,等待一次新生代GC再進(jìn)行后續(xù)操作。預(yù)清理后是重新標(biāo)記階段,由于重新標(biāo)記是獨(dú)占CPU的,如果新生代GC發(fā)生后,立即觸發(fā)一次重新標(biāo)記,那么一次停頓的時(shí)間可能會(huì)很長(zhǎng),為了避免這種情況,預(yù)處理會(huì)刻意等待一次新生代GC的發(fā)生,然后根據(jù)歷史性能數(shù)據(jù)預(yù)測(cè)下一次新生代GC可能發(fā)生的時(shí)間,在當(dāng)前時(shí)間和預(yù)測(cè)時(shí)間的中間時(shí)刻進(jìn)行重新標(biāo)記,這樣盡量避免新生代GC和重新標(biāo)記重合,盡可能減少一次停頓時(shí)間。

  • CMS-remark:重新標(biāo)記

  • CMS-concurrent-sweep:并發(fā)清除

  • CMS-concurrent-reset:并發(fā)重置

  • concurrent mode failure:CMS回收器并發(fā)回收失敗,這很可能是應(yīng)用程序在運(yùn)行過(guò)程中,垃圾未回收完成而新垃圾又在產(chǎn)生,導(dǎo)致老年代空間不夠造成的,可以考慮增加老年代空間,或者設(shè)置一個(gè)較小的-XX:CMSInitiatingOccupancyFraction參數(shù)。注意:如果在CMS的執(zhí)行過(guò)程中,已經(jīng)出現(xiàn)了內(nèi)存不足的情況,CMS回收就會(huì)失敗,虛擬機(jī)將啟動(dòng)老年代串行回收器進(jìn)行垃圾回收,此時(shí)應(yīng)用程序?qū)⑼耆袛?,直到垃圾回收完成,因此?yīng)盡量避免這種情況發(fā)生。

以上就是java中的串行并行及CMS垃圾回收器是怎樣的,小編相信有部分知識(shí)點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見到或用到的。希望你能通過(guò)這篇文章學(xué)到更多知識(shí)。更多詳情敬請(qǐng)關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI