關(guān)于GC之四-垃圾收集器實戰(zhàn)

JVM 垃圾回收器分類

新生代串行收集器

串行收集器主要有兩個特點(diǎn):第一,它僅僅使用單線程進(jìn)行垃圾回收;第二,它獨(dú)占式的垃圾回收。

在串行收集器進(jìn)行垃圾回收時,Java 應(yīng)用程序中的線程都需要暫停,等待垃圾回收的完成,這樣給用戶體驗造成較差效果。雖然如此,串行收集器卻是一個成熟、經(jīng)過長時間生產(chǎn)環(huán)境考驗的極為高效的收集器。新生代串行處理器使用復(fù)制算法,實現(xiàn)相對簡單,邏輯處理特別高效,且沒有線程切換的開銷。在諸如單 CPU 處理器或者較小的應(yīng)用內(nèi)存等硬件平臺不是特別優(yōu)越的場合,它的性能表現(xiàn)可以超過并行回收器和并發(fā)回收器。在 HotSpot 虛擬機(jī)中,使用-XX:+UseSerialGC 參數(shù)可以指定使用新生代串行收集器和老年代串行收集器。當(dāng) JVM 在 Client 模式下運(yùn)行時,它是默認(rèn)的垃圾收集器。一次新生代串行收集器的工作輸出日志類似如清單 1 信息 (使用-XX:+PrintGCDetails 開關(guān)) 所示。

清單 1. 一次新生代串行收集器的工作輸出日志
[GC [DefNew: 3468K->150K(9216K), 0.0028638 secs][Tenured:
  1562K->1712K(10240K), 0.0084220 secs] 3468K->1712K(19456K),
  [Perm : 377K->377K(12288K)],
  0.0113816 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]

它顯示了一次垃圾回收前的新生代的內(nèi)存占用量和垃圾回收后的新生代內(nèi)存占用量,以及垃圾回收所消耗的時間。

老年代串行收集器

老年代串行收集器使用的是標(biāo)記-壓縮算法。和新生代串行收集器一樣,它也是一個串行的、獨(dú)占式的垃圾回收器。由于老年代垃圾回收通常會使用比新生代垃圾回收更長的時間,因此,在堆空間較大的應(yīng)用程序中,一旦老年代串行收集器啟動,應(yīng)用程序很可能會因此停頓幾秒甚至更長時間。雖然如此,老年代串行回收器可以和多種新生代回收器配合使用,同時它也可以作為 CMS 回收器的備用回收器。若要啟用老年代串行回收器,可以嘗試使用以下參數(shù):-XX:+UseSerialGC: 新生代、老年代都使用串行回收器。

清單 2. 一次老年代串行收集器的工作輸出日志
Heap
 def new generation total 4928K, used 1373K [0x27010000, 0x27560000, 0x2c560000)
 eden space 4416K, 31% used [0x27010000, 0x27167628, 0x27460000)
 from space 512K, 0% used [0x27460000, 0x27460000, 0x274e0000)
 to space 512K, 0% used [0x274e0000, 0x274e0000, 0x27560000)
 tenured generation total 10944K, used 0K [0x2c560000, 0x2d010000, 0x37010000)
 the space 10944K, 0% used [0x2c560000, 0x2c560000, 0x2c560200, 0x2d010000)
 compacting perm gen total 12288K, used 376K [0x37010000, 0x37c10000, 0x3b010000)
 the space 12288K, 3% used [0x37010000, 0x3706e0b8, 0x3706e200, 0x37c10000)
 ro space 10240K, 51% used [0x3b010000, 0x3b543000, 0x3b543000, 0x3ba10000)
 rw space 12288K, 55% used [0x3ba10000, 0x3c0ae4f8, 0x3c0ae600, 0x3c610000)

如果使用-XX:+UseParNewGC 參數(shù)設(shè)置,表示新生代使用并行收集器,老年代使用串行收集器,如清單 3 所示。

清單 3. 一次串并行收集器混合使用的工作輸出日志
Heap
 par new generation total 4928K, used 1373K [0x0f010000, 0x0f560000, 0x14560000)
 eden space 4416K, 31% used [0x0f010000, 0x0f167620, 0x0f460000)
 from space 512K, 0% used [0x0f460000, 0x0f460000, 0x0f4e0000)
 to space 512K, 0% used [0x0f4e0000, 0x0f4e0000, 0x0f560000)
 tenured generation total 10944K, used 0K [0x14560000, 0x15010000, 0x1f010000)
 the space 10944K, 0% used [0x14560000, 0x14560000, 0x14560200, 0x15010000)
 compacting perm gen total 12288K, used 2056K [0x1f010000, 0x1fc10000, 0x23010000)
 the space 12288K, 16% used [0x1f010000, 0x1f2121d0, 0x1f212200, 0x1fc10000)
No shared spaces configured.

如果使用-XX:+UseParallelGC 參數(shù)設(shè)置,表示新生代和老年代均使用并行回收收集器。如清單 4 所示。

清單 4. 一次老年代并行回收器的工作輸出日志
[Full GC [Tenured: 1712K->1699K(10240K), 0.0071963 secs] 1712K->1699K(19456K),
      [Perm : 377K->372K(12288K)], 0.0072393 secs] [Times: user=0.00 sys=0.00,
      real=0.01 secs]

它顯示了垃圾回收前老年代和永久區(qū)的內(nèi)存占用量,以及垃圾回收后老年代和永久區(qū)的內(nèi)存使用量。

并行收集器

并行收集器是工作在新生代的垃圾收集器,它只簡單地將串行回收器多線程化。它的回收策略、算法以及參數(shù)和串行回收器一樣。

并行回收器也是獨(dú)占式的回收器,在收集過程中,應(yīng)用程序會全部暫停。但由于并行回收器使用多線程進(jìn)行垃圾回收,因此,在并發(fā)能力比較強(qiáng)的 CPU 上,它產(chǎn)生的停頓時間要短于串行回收器,而在單 CPU 或者并發(fā)能力較弱的系統(tǒng)中,并行回收器的效果不會比串行回收器好,由于多線程的壓力,它的實際表現(xiàn)很可能比串行回收器差。

開啟并行回收器可以使用參數(shù)-XX:+UseParNewGC,該參數(shù)設(shè)置新生代使用并行收集器,老年代使用串行收集器。

清單 5. 設(shè)置參數(shù)-XX:+UseParNewGC 的輸出日志
[GC [ParNew: 825K->161K(4928K), 0.0155258 secs][Tenured: 8704K->661K(10944K), 
  0.0071964 secs] 9017K->661K(15872K), 
  [Perm : 2049K->2049K(12288K)], 0.0228090 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
Heap
 par new generation total 4992K, used 179K [0x0f010000, 0x0f570000, 0x14560000)
 eden space 4480K, 4% used [0x0f010000, 0x0f03cda8, 0x0f470000)
 from space 512K, 0% used [0x0f470000, 0x0f470000, 0x0f4f0000)
 to space 512K, 0% used [0x0f4f0000, 0x0f4f0000, 0x0f570000)
 tenured generation total 10944K, used 8853K [0x14560000, 0x15010000, 0x1f010000)
 the space 10944K, 80% used [0x14560000, 0x14e057c0, 0x14e05800, 0x15010000)
 compacting perm gen total 12288K, used 2060K [0x1f010000, 0x1fc10000, 0x23010000)
 the space 12288K, 16% used [0x1f010000, 0x1f213228, 0x1f213400, 0x1fc10000)
No shared spaces configured.

設(shè)置參數(shù)-XX:+UseConcMarkSweepGC 可以要求新生代使用并行收集器,老年代使用 CMS。

清單 6. 設(shè)置參數(shù)-XX:+UseConcMarkSweepGC 的輸出日志
[GC [ParNew: 8967K->669K(14784K), 0.0040895 secs] 8967K->669K(63936K),
  0.0043255 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 par new generation total 14784K, used 9389K [0x03f50000, 0x04f50000, 0x04f50000)
 eden space 13184K, 66% used [0x03f50000, 0x047d3e58, 0x04c30000)
 from space 1600K, 41% used [0x04dc0000, 0x04e67738, 0x04f50000)
 to space 1600K, 0% used [0x04c30000, 0x04c30000, 0x04dc0000)
 concurrent mark-sweep generation total 49152K, used 0K [0x04f50000, 0x07f50000, 0x09f50000)
 concurrent-mark-sweep perm gen total 12288K, used 2060K [0x09f50000, 0x0ab50000, 0x0df50000)

并行收集器工作時的線程數(shù)量可以使用-XX:ParallelGCThreads 參數(shù)指定。一般,最好與 CPU 數(shù)量相當(dāng),避免過多的線程數(shù)影響垃圾收集性能。在默認(rèn)情況下,當(dāng) CPU 數(shù)量小于 8 個,ParallelGCThreads 的值等于 CPU 數(shù)量,大于 8 個,ParallelGCThreads 的值等于 3+[5*CPU_Count]/8]。以下測試顯示了筆者筆記本上運(yùn)行 8 個線程時耗時最短,本人筆記本是 8 核 IntelCPU。

清單 7. 設(shè)置為 8 個線程后 GC 輸出
[GC [ParNew: 8967K->676K(14784K), 0.0036983 secs] 8967K->676K(63936K),
  0.0037662 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 par new generation total 14784K, used 9395K [0x040e0000, 0x050e0000, 0x050e0000)
 eden space 13184K, 66% used [0x040e0000, 0x04963e58, 0x04dc0000)
 from space 1600K, 42% used [0x04f50000, 0x04ff9100, 0x050e0000)
 to space 1600K, 0% used [0x04dc0000, 0x04dc0000, 0x04f50000)
 concurrent mark-sweep generation total 49152K, used 0K [0x050e0000, 0x080e0000, 0x0a0e0000)
 concurrent-mark-sweep perm gen total 12288K, used 2060K [0x0a0e0000, 0x0ace0000, 0x0e0e0000)

清單 8. 設(shè)置為 128 個線程后 GC 輸出
[GC [ParNew: 8967K->664K(14784K), 0.0207327 secs] 8967K->664K(63936K),
 0.0208077 secs] [Times: user=0.03 sys=0.00, real=0.02 secs]
清單 9. 設(shè)置為 640 個線程后 GC 輸出
[GC [ParNew: 8967K->667K(14784K), 0.2323704 secs] 8967K->667K(63936K),
 0.2324778 secs] [Times: user=0.34 sys=0.02, real=0.23 secs]
清單 10. 設(shè)置為 1280 個線程后 GC 輸出
Error occurred during initialization of VM`

Too small new size specified`

新生代并行回收 (Parallel Scavenge) 收集器

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

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

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

-XX:+UseParallelOldGC:新生代和老年代都是用并行回收收集器。

清單 11. 設(shè)置為 24 個線程后 GC 輸出
Heap
 PSYoungGen total 4800K, used 893K [0x1dac0000, 0x1e010000, 0x23010000)
 eden space 4160K, 21% used [0x1dac0000,0x1db9f570,0x1ded0000)
 from space 640K, 0% used [0x1df70000,0x1df70000,0x1e010000)
 to space 640K, 0% used [0x1ded0000,0x1ded0000,0x1df70000)
 ParOldGen total 19200K, used 16384K [0x13010000, 0x142d0000, 0x1dac0000)
 object space 19200K, 85% used [0x13010000,0x14010020,0x142d0000)
 PSPermGen total 12288K, used 2054K [0x0f010000, 0x0fc10000, 0x13010000)
 object space 12288K, 16% used [0x0f010000,0x0f2119c0,0x0fc10000)

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

-XX:+MaxGCPauseMills:設(shè)置最大垃圾收集停頓時間,它的值是一個大于 0 的整數(shù)。收集器在工作時會調(diào)整 Java 堆大小或者其他一些參數(shù),盡可能地把停頓時間控制在 MaxGCPauseMills 以內(nèi)。如果希望減少停頓時間,而把這個值設(shè)置得很小,為了達(dá)到預(yù)期的停頓時間,JVM 可能會使用一個較小的堆 (一個小堆比一個大堆回收快),而這將導(dǎo)致垃圾回收變得很頻繁,從而增加了垃圾回收總時間,降低了吞吐量。

-XX:+GCTimeRatio:設(shè)置吞吐量大小,它的值是一個 0-100 之間的整數(shù)。假設(shè) GCTimeRatio 的值為 n,那么系統(tǒng)將花費(fèi)不超過 1/(1+n) 的時間用于垃圾收集。比如 GCTimeRatio 等于 19,則系統(tǒng)用于垃圾收集的時間不超過 1/(1+19)=5%。默認(rèn)情況下,它的取值是 99,即不超過 1%的時間用于垃圾收集。

除此之外,并行回收收集器與并行收集器另一個不同之處在于,它支持一種自適應(yīng)的 GC 調(diào)節(jié)策略,使用-XX:+UseAdaptiveSizePolicy 可以打開自適應(yīng) GC 策略。在這種模式下,新生代的大小、eden 和 survivor 的比例、晉升老年代的對象年齡等參數(shù)會被自動調(diào)整,以達(dá)到在堆大小、吞吐量和停頓時間之間的平衡點(diǎn)。在手工調(diào)優(yōu)比較困難的場合,可以直接使用這種自適應(yīng)的方式,僅指定虛擬機(jī)的最大堆、目標(biāo)的吞吐量 (GCTimeRatio) 和停頓時間 (MaxGCPauseMills),讓虛擬機(jī)自己完成調(diào)優(yōu)工作。

清單 12. 新生代并行回收收集器 GC 輸出
Heap
 PSYoungGen total 4800K, used 893K [0x1dac0000, 0x1e010000, 0x23010000)
 eden space 4160K, 21% used [0x1dac0000,0x1db9f570,0x1ded0000)
 from space 640K, 0% used [0x1df70000,0x1df70000,0x1e010000)
 to space 640K, 0% used [0x1ded0000,0x1ded0000,0x1df70000)
 PSOldGen total 19200K, used 16384K [0x13010000, 0x142d0000, 0x1dac0000)
 object space 19200K, 85% used [0x13010000,0x14010020,0x142d0000)
 PSPermGen total 12288K, used 2054K [0x0f010000, 0x0fc10000, 0x13010000)
 object space 12288K, 16% used [0x0f010000,0x0f2119c0,0x0fc10000)

它也顯示了收集器的工作成果,也就是回收前的內(nèi)存大小和回收后的內(nèi)存大小,以及花費(fèi)的時間。

老年代并行回收收集器

老年代的并行回收收集器也是一種多線程并發(fā)的收集器。和新生代并行回收收集器一樣,它也是一種關(guān)注吞吐量的收集器。老年代并行回收收集器使用標(biāo)記-壓縮算法,JDK1.6 之后開始啟用。

使用-XX:+UseParallelOldGC 可以在新生代和老生代都使用并行回收收集器,這是一對非常關(guān)注吞吐量的垃圾收集器組合,在對吞吐量敏感的系統(tǒng)中,可以考慮使用。參數(shù)-XX:ParallelGCThreads 也可以用于設(shè)置垃圾回收時的線程數(shù)量。

清單 13 是設(shè)置線程數(shù)量為 100 時老年代并行回收收集器輸出日志:

清單 13. 老年代并行回收收集器設(shè)置 100 線程時 GC 輸出
Heap
 PSYoungGen total 4800K, used 893K [0x1dac0000, 0x1e010000, 0x23010000)
 eden space 4160K, 21% used [0x1dac0000,0x1db9f570,0x1ded0000)
 from space 640K, 0% used [0x1df70000,0x1df70000,0x1e010000)
 to space 640K, 0% used [0x1ded0000,0x1ded0000,0x1df70000)
 ParOldGen total 19200K, used 16384K [0x13010000, 0x142d0000, 0x1dac0000)
 object space 19200K, 85% used [0x13010000,0x14010020,0x142d0000)
 PSPermGen total 12288K, used 2054K [0x0f010000, 0x0fc10000, 0x13010000)
 object space 12288K, 16% used [0x0f010000,0x0f2119c0,0x0fc10000)

CMS 收集器

與并行回收收集器不同,CMS 收集器主要關(guān)注于系統(tǒng)停頓時間。CMS 是 Concurrent Mark Sweep 的縮寫,意為并發(fā)標(biāo)記清除,從名稱上可以得知,它使用的是標(biāo)記-清除算法,同時它又是一個使用多線程并發(fā)回收的垃圾收集器。

CMS 工作時,主要步驟有:初始標(biāo)記、并發(fā)標(biāo)記、重新標(biāo)記、并發(fā)清除和并發(fā)重置。其中初始標(biāo)記和重新標(biāo)記是獨(dú)占系統(tǒng)資源的,而并發(fā)標(biāo)記、并發(fā)清除和并發(fā)重置是可以和用戶線程一起執(zhí)行的。因此,從整體上來說,CMS 收集不是獨(dú)占式的,它可以在應(yīng)用程序運(yùn)行過程中進(jìn)行垃圾回收。

根據(jù)標(biāo)記-清除算法,初始標(biāo)記、并發(fā)標(biāo)記和重新標(biāo)記都是為了標(biāo)記出需要回收的對象。并發(fā)清理則是在標(biāo)記完成后,正式回收垃圾對象;并發(fā)重置是指在垃圾回收完成后,重新初始化 CMS 數(shù)據(jù)結(jié)構(gòu)和數(shù)據(jù),為下一次垃圾回收做好準(zhǔn)備。并發(fā)標(biāo)記、并發(fā)清理和并發(fā)重置都是可以和應(yīng)用程序線程一起執(zhí)行的。

CMS 收集器在其主要的工作階段雖然沒有暴力地徹底暫停應(yīng)用程序線程,但是由于它和應(yīng)用程序線程并發(fā)執(zhí)行,相互搶占 CPU,所以在 CMS 執(zhí)行期內(nèi)對應(yīng)用程序吞吐量造成一定影響。CMS 默認(rèn)啟動的線程數(shù)是 (ParallelGCThreads+3)/4),ParallelGCThreads 是新生代并行收集器的線程數(shù),也可以通過-XX:ParallelCMSThreads 參數(shù)手工設(shè)定 CMS 的線程數(shù)量。當(dāng) CPU 資源比較緊張時,受到 CMS 收集器線程的影響,應(yīng)用程序的性能在垃圾回收階段可能會非常糟糕。

由于 CMS 收集器不是獨(dú)占式的回收器,在 CMS 回收過程中,應(yīng)用程序仍然在不停地工作。在應(yīng)用程序工作過程中,又會不斷地產(chǎn)生垃圾。這些新生成的垃圾在當(dāng)前 CMS 回收過程中是無法清除的。同時,因為應(yīng)用程序沒有中斷,所以在 CMS 回收過程中,還應(yīng)該確保應(yīng)用程序有足夠的內(nèi)存可用。因此,CMS 收集器不會等待堆內(nèi)存飽和時才進(jìn)行垃圾回收,而是當(dāng)前堆內(nèi)存使用率達(dá)到某一閾值時,便開始進(jìn)行回收,以確保應(yīng)用程序在 CMS 工作過程中依然有足夠的空間支持應(yīng)用程序運(yùn)行。

這個回收閾值可以使用-XX:CMSInitiatingOccupancyFraction 來指定,默認(rèn)是 68。即當(dāng)老年代的空間使用率達(dá)到 68%時,會執(zhí)行一次 CMS 回收。如果應(yīng)用程序的內(nèi)存使用率增長很快,在 CMS 的執(zhí)行過程中,已經(jīng)出現(xiàn)了內(nèi)存不足的情況,此時,CMS 回收將會失敗,JVM 將啟動老年代串行收集器進(jìn)行垃圾回收。如果這樣,應(yīng)用程序?qū)⑼耆袛啵钡嚼占瓿?,這時,應(yīng)用程序的停頓時間可能很長。因此,根據(jù)應(yīng)用程序的特點(diǎn),可以對-XX:CMSInitiatingOccupancyFraction 進(jìn)行調(diào)優(yōu)。如果內(nèi)存增長緩慢,則可以設(shè)置一個稍大的值,大的閾值可以有效降低 CMS 的觸發(fā)頻率,減少老年代回收的次數(shù)可以較為明顯地改善應(yīng)用程序性能。反之,如果應(yīng)用程序內(nèi)存使用率增長很快,則應(yīng)該降低這個閾值,以避免頻繁觸發(fā)老年代串行收集器。

標(biāo)記-清除算法將會造成大量內(nèi)存碎片,離散的可用空間無法分配較大的對象。在這種情況下,即使堆內(nèi)存仍然有較大的剩余空間,也可能會被迫進(jìn)行一次垃圾回收,以換取一塊可用的連續(xù)內(nèi)存,這種現(xiàn)象對系統(tǒng)性能是相當(dāng)不利的,為了解決這個問題,CMS 收集器還提供了幾個用于內(nèi)存壓縮整理的算法。

-XX:+UseCMSCompactAtFullCollection 參數(shù)可以使 CMS 在垃圾收集完成后,進(jìn)行一次內(nèi)存碎片整理。內(nèi)存碎片的整理并不是并發(fā)進(jìn)行的。-XX:CMSFullGCsBeforeCompaction 參數(shù)可以用于設(shè)定進(jìn)行多少次 CMS 回收后,進(jìn)行一次內(nèi)存壓縮。

-XX:CMSInitiatingOccupancyFraction 設(shè)置為 100,同時設(shè)置-XX:+UseCMSCompactAtFullCollection 和-XX:CMSFullGCsBeforeCompaction,日志輸出如下:

清單 14.CMS 垃圾回收器 GC 輸出
[GC [DefNew: 825K->149K(4928K), 0.0023384 secs][Tenured: 8704K->661K(10944K),
  0.0587725 secs] 9017K->661K(15872K), 
  [Perm : 374K->374K(12288K)], 0.0612037 secs] [Times: user=0.01 sys=0.02, real=0.06 secs] 
Heap
 def new generation total 4992K, used 179K [0x27010000, 0x27570000, 0x2c560000)
 eden space 4480K, 4% used [0x27010000, 0x2703cda8, 0x27470000)
 from space 512K, 0% used [0x27470000, 0x27470000, 0x274f0000)
 to space 512K, 0% used [0x274f0000, 0x274f0000, 0x27570000)
 tenured generation total 10944K, used 8853K [0x2c560000, 0x2d010000, 0x37010000)
 the space 10944K, 80% used [0x2c560000, 0x2ce057c8, 0x2ce05800, 0x2d010000)
 compacting perm gen total 12288K, used 374K [0x37010000, 0x37c10000, 0x3b010000)
 the space 12288K, 3% used [0x37010000, 0x3706db10, 0x3706dc00, 0x37c10000)
 ro space 10240K, 51% used [0x3b010000, 0x3b543000, 0x3b543000, 0x3ba10000)
 rw space 12288K, 55% used [0x3ba10000, 0x3c0ae4f8, 0x3c0ae600, 0x3c610000)

G1 收集器 (Garbage First)

G1 收集器的目標(biāo)是作為一款服務(wù)器的垃圾收集器,因此,它在吞吐量和停頓控制上,預(yù)期要優(yōu)于 CMS 收集器。

與 CMS 收集器相比,G1 收集器是基于標(biāo)記-壓縮算法的。因此,它不會產(chǎn)生空間碎片,也沒有必要在收集完成后,進(jìn)行一次獨(dú)占式的碎片整理工作。G1 收集器還可以進(jìn)行非常精確的停頓控制。它可以讓開發(fā)人員指定當(dāng)停頓時長為 M 時,垃圾回收時間不超過 N。使用參數(shù)-XX:+UnlockExperimentalVMOptions –XX:+UseG1GC 來啟用 G1 回收器,設(shè)置 G1 回收器的目標(biāo)停頓時間:-XX:MaxGCPauseMills=20,-XX:GCPauseIntervalMills=200。

收集器對系統(tǒng)性能的影響

通過清單 15 所示代碼運(yùn)行 1 萬次循環(huán),每次分配 512*100B 空間,采用不同的垃圾回收器,輸出程序運(yùn)行所消耗的時間。

清單 15. 性能測試代碼
import java.util.HashMap;
import java.util.Map;

public class GCTimeTest {

    static Map map = new HashMap();

    public static void main(String[] args){
        long beginTime = System.currentTimeMillis();

        for (int i = 0; i < 100000; i++){
            if(map.size() * 512 / 1024 / 1024 > 100 ){ // 需要計算map.size 不內(nèi)存溢出
                map.clear();  // 保護(hù)內(nèi)存不溢出
                System.out.println("clean map");
            }

            byte[] bytes;

            for(int j = 0; j < 100; j++){
                bytes = new byte[512];
                map.put(System.nanoTime(),bytes);
            }
        }

        long endTime = System.currentTimeMillis();
        System.out.println("spendTime:" + (endTime - beginTime));

    }
}

在指定128M的堆空間后,其分代數(shù)據(jù)如下:

Snip20180529_3.png

每次map.clear 之后,由于需要大量回收old區(qū)的空間,都需要進(jìn)行一次full gc。下面使用不同的gc參數(shù)試驗:

使用參數(shù)-Xmx128M -Xms128M -XX:+UseParNewGC -XX:+PrintGCDetails運(yùn)行代碼,輸出如下:
Java HotSpot(TM) 64-Bit Server VM warning: Using the ParNew young collector with the Serial old collector is deprecated and will likely be removed in a future release
[GC (Allocation Failure) [ParNew: 34944K->4352K(39296K), 0.0447096 secs] 34944K->33418K(126720K), 0.0447417 secs] [Times: user=0.07 sys=0.03, real=0.04 secs] 
[GC (Allocation Failure) [ParNew: 39296K->4352K(39296K), 0.0366802 secs] 68362K->68325K(126720K), 0.0367088 secs] [Times: user=0.07 sys=0.03, real=0.04 secs] 
[GC (Allocation Failure) [ParNew: 39296K->39296K(39296K), 0.0000154 secs][Tenured: 63973K->87423K(87424K), 0.1034759 secs] 103269K->103257K(126720K), [Metaspace: 3310K->3310K(1056768K)], 0.1035723 secs] [Times: user=0.09 sys=0.01, real=0.11 secs] 
clean map
[Full GC (Allocation Failure) [Tenured: 87423K->4769K(87424K), 0.0194451 secs] 126719K->4769K(126720K), [Metaspace: 3314K->3314K(1056768K)], 0.0194773 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 

.........多次gc & fullgc

[GC (Allocation Failure) [ParNew: 34944K->4350K(39296K), 0.0091677 secs] 43145K->43224K(126720K), 0.0091926 secs] [Times: user=0.04 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [ParNew: 39294K->4352K(39296K), 0.0112309 secs] 78168K->78249K(126720K), 0.0112541 secs] [Times: user=0.04 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [ParNew: 39296K->39296K(39296K), 0.0000211 secs][Tenured: 73897K->87423K(87424K), 0.0903542 secs] 113193K->113190K(126720K), [Metaspace: 3316K->3316K(1056768K)], 0.0904173 secs] [Times: user=0.09 sys=0.00, real=0.09 secs] 
clean map
[Full GC (Allocation Failure) [Tenured: 87423K->8525K(87424K), 0.0231646 secs] 126719K->8525K(126720K), [Metaspace: 3316K->3316K(1056768K)], 0.0232335 secs] [Times: user=0.03 sys=0.00, real=0.03 secs] 
spendTime:9499
Heap
 par new generation   total 39296K, used 34231K [0x00000007b8000000, 0x00000007baaa0000, 0x00000007baaa0000)
  eden space 34944K,  97% used [0x00000007b8000000, 0x00000007ba16de98, 0x00000007ba220000)
  from space 4352K,   0% used [0x00000007ba220000, 0x00000007ba220000, 0x00000007ba660000)
  to   space 4352K,   0% used [0x00000007ba660000, 0x00000007ba660000, 0x00000007baaa0000)
 tenured generation   total 87424K, used 8525K [0x00000007baaa0000, 0x00000007c0000000, 0x00000007c0000000)
   the space 87424K,   9% used [0x00000007baaa0000, 0x00000007bb2f34f0, 0x00000007bb2f3600, 0x00000007c0000000)
 Metaspace       used 3322K, capacity 4500K, committed 4864K, reserved 1056768K
  class space    used 366K, capacity 388K, committed 512K, reserved 1048576K

可以看到

  • serial old gc 已經(jīng)不推薦使用。
  • 共用了9499ms
  • 前面兩次gc,young代的對象都轉(zhuǎn)移到了old代
  • 第三次gc,young代的占用空間并沒有見減小,old代的占用空間反而增大了,說明young代已經(jīng)無法為新對象分配內(nèi)存,而且由于存在跨代引用,young區(qū)原有對象無法被回收。此時整個old代的內(nèi)存空間也已經(jīng)占用了超過 68362/87424K = 80%
  • 此時如果不執(zhí)行map.clear,那么就會出現(xiàn)OOM的問題。而執(zhí)行了map.clear,由于需要回收大量young&old代的內(nèi)存,執(zhí)行了一次fullgc。
使用參數(shù)-Xmx128M -Xms128M -XX:+UseParallelOldGC –XX:ParallelGCThreads=8 -XX:+PrintGCDetails運(yùn)行代碼,輸出如下:
[GC (Allocation Failure) [PSYoungGen: 33280K->5104K(38400K)] 33280K->30576K(125952K), 0.0226151 secs] [Times: user=0.04 sys=0.02, real=0.02 secs] 
[GC (Allocation Failure) [PSYoungGen: 38384K->5104K(38400K)] 63856K->63152K(125952K), 0.0259692 secs] [Times: user=0.06 sys=0.03, real=0.02 secs] 
[Full GC (Ergonomics) [PSYoungGen: 5104K->0K(38400K)] [ParOldGen: 58048K->62865K(87552K)] 63152K->62865K(125952K), [Metaspace: 3310K->3310K(1056768K)], 0.0824536 secs] [Times: user=0.20 sys=0.00, real=0.08 secs] 
[Full GC (Ergonomics) [PSYoungGen: 33280K->8186K(38400K)] [ParOldGen: 62865K->87271K(87552K)] 96145K->95458K(125952K), [Metaspace: 3310K->3310K(1056768K)], 0.0882627 secs] [Times: user=0.22 sys=0.03, real=0.09 secs] 
clean map1659
[Full GC (Ergonomics) [PSYoungGen: 33280K->0K(38400K)] [ParOldGen: 87271K->25935K(87552K)] 120551K->25935K(125952K), [Metaspace: 3312K->3312K(1056768K)], 0.0375286 secs] [Times: user=0.07 sys=0.00, real=0.04 secs] 

...... gc & fullgc

clean map99540
[Full GC (Ergonomics) [PSYoungGen: 14848K->0K(29184K)] [ParOldGen: 85192K->5496K(87552K)] 100040K->5496K(116736K), [Metaspace: 3318K->3318K(1056768K)], 0.0096408 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 14848K->14336K(29184K)] 20344K->20408K(116736K), 0.0063851 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
spendTime:14181
Heap
 PSYoungGen      total 29184K, used 22355K [0x00000007bd580000, 0x00000007c0000000, 0x00000007c0000000)
  eden space 14848K, 54% used [0x00000007bd580000,0x00000007bdd54f00,0x00000007be400000)
  from space 14336K, 100% used [0x00000007be400000,0x00000007bf200000,0x00000007bf200000)
  to   space 14336K, 0% used [0x00000007bf200000,0x00000007bf200000,0x00000007c0000000)
 ParOldGen       total 87552K, used 6072K [0x00000007b8000000, 0x00000007bd580000, 0x00000007bd580000)
  object space 87552K, 6% used [0x00000007b8000000,0x00000007b85ee348,0x00000007bd580000)
 Metaspace       used 3325K, capacity 4500K, committed 4864K, reserved 1056768K
  class space    used 366K, capacity 388K, committed 512K, reserved 1048576K

可以看到

  • young代默認(rèn)是用了Parallel gc 算法
  • 這里把 >100 改成了 > 80,不然會很快OOM ,說明在par old gc釋放內(nèi)存的速度滿足不了分配的速度
  • 這里由于map.clear執(zhí)行次數(shù)比之前多,總體時間已經(jīng)不具備太多參考性,不過總體看共耗時14181ms,比Serial old gc 耗時要長
使用參數(shù)-Xmx128M -Xms128M -XX:+UseSerialGC -XX:+PrintGCDetails運(yùn)行代碼,輸出如下:
[GC (Allocation Failure) [DefNew: 34944K->4352K(39296K), 0.0594770 secs] 34944K->32056K(126720K), 0.0595147 secs] [Times: user=0.04 sys=0.01, real=0.06 secs] 
[GC (Allocation Failure) [DefNew: 39296K->4351K(39296K), 0.0821899 secs] 67000K->66118K(126720K), 0.0822201 secs] [Times: user=0.06 sys=0.01, real=0.08 secs] 
[GC (Allocation Failure) [DefNew: 39295K->39295K(39296K), 0.0000178 secs][Tenured: 61766K->87423K(87424K), 0.1293997 secs] 101062K->101052K(126720K), [Metaspace: 3310K->3310K(1056768K)], 0.1294609 secs] [Times: user=0.11 sys=0.01, real=0.13 secs] 
clean map2069
[Full GC (Allocation Failure) [Tenured: 87423K->7672K(87424K), 0.0258848 secs] 126719K->7672K(126720K), [Metaspace: 3316K->3316K(1056768K)], 0.0259244 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 

...... gc & full gc

[GC (Allocation Failure) [DefNew: 34944K->4351K(39296K), 0.0299485 secs] 43661K->43649K(126720K), 0.0299941 secs] [Times: user=0.03 sys=0.00, real=0.03 secs] 
[GC (Allocation Failure) [DefNew: 39295K->4351K(39296K), 0.0345476 secs] 78593K->78591K(126720K), 0.0345743 secs] [Times: user=0.03 sys=0.00, real=0.04 secs] 
[GC (Allocation Failure) [DefNew: 39295K->39295K(39296K), 0.0000159 secs][Tenured: 74239K->87423K(87424K), 0.1011070 secs] 113535K->113507K(126720K), [Metaspace: 3318K->3318K(1056768K)], 0.1011625 secs] [Times: user=0.10 sys=0.00, real=0.10 secs] 
clean map99312
[Full GC (Allocation Failure) [Tenured: 87423K->8717K(87424K), 0.0285509 secs] 126719K->8717K(126720K), [Metaspace: 3318K->3318K(1056768K)], 0.0285818 secs] [Times: user=0.03 sys=0.00, real=0.02 secs] 
spendTime:11436
Heap
 def new generation   total 39296K, used 34108K [0x00000007b8000000, 0x00000007baaa0000, 0x00000007baaa0000)
  eden space 34944K,  97% used [0x00000007b8000000, 0x00000007ba14f100, 0x00000007ba220000)
  from space 4352K,   0% used [0x00000007ba220000, 0x00000007ba220000, 0x00000007ba660000)
  to   space 4352K,   0% used [0x00000007ba660000, 0x00000007ba660000, 0x00000007baaa0000)
 tenured generation   total 87424K, used 8717K [0x00000007baaa0000, 0x00000007c0000000, 0x00000007c0000000)
   the space 87424K,   9% used [0x00000007baaa0000, 0x00000007bb323428, 0x00000007bb323600, 0x00000007c0000000)
 Metaspace       used 3324K, capacity 4500K, committed 4864K, reserved 1056768K
  class space    used 366K, capacity 388K, committed 512K, reserved 1048576K

說明:

  • 依然把>100復(fù)原,并不會產(chǎn)生OOM
  • 新生代關(guān)鍵字變成了DefNew,而不是ParNew,說明young代用的是串行回收
  • 每次map.clear之后依然進(jìn)行一次fullgc
  • 共耗時 11436ms,比單純的serial gc 耗時長

PS:補(bǔ)充下各種關(guān)鍵字的說明

串行收集器: DefNew:是使用-XX:+UseSerialGC(新生代,老年代都使用串行回收收集器)。
并行收集器: ParNew:是使用-XX:+UseParNewGC(新生代使用并行收集器,老年代使用串行回收收集器)或者-XX:+UseConcMarkSweepGC(新生代使用并行收集器,老年代使用CMS)。
PSYoungGen:是使用-XX:+UseParallelOldGC(新生代,老年代都使用并行回收收集器)或者-XX:+UseParallelGC(新生代使用并行回收收集器,老年代使用串行收集器)
garbage-first heap:是使用-XX:+UseG1GC(G1收集器)
使用參數(shù)-Xmx512M -Xms512M -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails運(yùn)行代碼,輸出如下:
[GC (Allocation Failure) [ParNew: 34944K->4352K(39296K), 0.0567471 secs] 34944K->32209K(126720K), 0.0567964 secs] [Times: user=0.12 sys=0.04, real=0.06 secs] 
[GC (Allocation Failure) [ParNew: 39296K->4352K(39296K), 0.0403496 secs] 67153K->66132K(126720K), 0.0403796 secs] [Times: user=0.08 sys=0.03, real=0.04 secs] 
[GC (CMS Initial Mark) [1 CMS-initial-mark: 61780K(87424K)] 66822K(126720K), 0.0028364 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
[CMS-concurrent-mark-start]
[GC (Allocation Failure) [ParNew: 39296K->39296K(39296K), 0.0000236 secs][CMS[CMS-concurrent-mark: 0.031/0.031 secs] [Times: user=0.05 sys=0.00, real=0.03 secs] 
 (concurrent mode failure): 61780K->87423K(87424K), 0.1991105 secs] 101076K->101043K(126720K), [Metaspace: 3311K->3311K(1056768K)], 0.1991834 secs] [Times: user=0.14 sys=0.01, real=0.20 secs] 
clean map2069
[Full GC (Allocation Failure) [CMS: 87423K->6985K(87424K), 0.0341552 secs] 126719K->6985K(126720K), [Metaspace: 3316K->3316K(1056768K)], 0.0342075 secs] [Times: user=0.02 sys=0.00, real=0.04 secs] 
[GC (Allocation Failure) [ParNew: 34944K->4352K(39296K), 0.0247864 secs] 41929K->41936K(126720K), 0.0248217 secs] [Times: user=0.05 sys=0.00, real=0.02 secs] 
[GC (Allocation Failure) [ParNew: 39296K->4352K(39296K), 0.0250843 secs] 76880K->76870K(126720K), 0.0251154 secs] [Times: user=0.07 sys=0.00, real=0.02 secs] 
[GC (CMS Initial Mark) [1 CMS-initial-mark: 72518K(87424K)] 77538K(126720K), 0.0007828 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[CMS-concurrent-mark-start]
[GC (Allocation Failure) [ParNew: 39296K->39296K(39296K), 0.0000267 secs][CMS[CMS-concurrent-mark: 0.039/0.039 secs] [Times: user=0.05 sys=0.00, real=0.04 secs] 
 (concurrent mode failure): 72518K->87423K(87424K), 0.1825655 secs] 111814K->111783K(126720K), [Metaspace: 3316K->3316K(1056768K)], 0.1826484 secs] [Times: user=0.14 sys=0.00, real=0.19 secs] 
clean map4138
[Full GC (Allocation Failure) [CMS: 87423K->8717K(87424K), 0.0251545 secs] 126719K->8717K(126720K), [Metaspace: 3316K->3316K(1056768K)], 0.0252056 secs] [Times: user=0.02 sys=0.00, real=0.03 secs] 

...... gc & fullgc

[GC (Allocation Failure) [ParNew: 34944K->4350K(39296K), 0.0347229 secs] 43660K->44521K(126720K), 0.0347537 secs] [Times: user=0.08 sys=0.00, real=0.03 secs] 
[GC (Allocation Failure) [ParNew: 39294K->4352K(39296K), 0.0328985 secs] 79465K->79655K(126720K), 0.0329299 secs] [Times: user=0.09 sys=0.00, real=0.03 secs] 
[GC (CMS Initial Mark) [1 CMS-initial-mark: 75303K(87424K)] 80276K(126720K), 0.0007394 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[CMS-concurrent-mark-start]
[GC (Allocation Failure) [ParNew: 39296K->39296K(39296K), 0.0000210 secs][CMS[CMS-concurrent-mark: 0.029/0.029 secs] [Times: user=0.04 sys=0.00, real=0.03 secs] 
 (concurrent mode failure): 75303K->87423K(87424K), 0.1324974 secs] 114599K->113496K(126720K), [Metaspace: 3318K->3318K(1056768K)], 0.1325644 secs] [Times: user=0.13 sys=0.01, real=0.14 secs] 
clean map99312
[Full GC (Allocation Failure) [CMS: 87423K->8717K(87424K), 0.0350586 secs] 126719K->8717K(126720K), [Metaspace: 3318K->3318K(1056768K)], 0.0351243 secs] [Times: user=0.03 sys=0.00, real=0.03 secs] 
spendTime:13169
Heap
 par new generation   total 39296K, used 34111K [0x00000007b8000000, 0x00000007baaa0000, 0x00000007baaa0000)
  eden space 34944K,  97% used [0x00000007b8000000, 0x00000007ba14fd30, 0x00000007ba220000)
  from space 4352K,   0% used [0x00000007ba220000, 0x00000007ba220000, 0x00000007ba660000)
  to   space 4352K,   0% used [0x00000007ba660000, 0x00000007ba660000, 0x00000007baaa0000)
 concurrent mark-sweep generation total 87424K, used 8717K [0x00000007baaa0000, 0x00000007c0000000, 0x00000007c0000000)
 Metaspace       used 3324K, capacity 4500K, committed 4864K, reserved 1056768K
  class space    used 366K, capacity 388K, committed 512K, reserved 1048576K

說明:

  • 總耗時13169 ,說明在堆空間比較小的時候,cms并不是好的選擇

  • 可以看到,young代的回收機(jī)制依然是parallel gc

  • concurrent mode failure出現(xiàn)了,說明old代在gc時留給程序運(yùn)行的內(nèi)存空間太小,這時會執(zhí)行serial old,會比較耗時。應(yīng)該調(diào)整觸發(fā)full gc 的old區(qū)參數(shù),默認(rèn)是68%,比如可以設(shè)置為50%左右,耗時變?yōu)?2957,不過full gc 觸發(fā)的次數(shù)會變大。

  • -Xmx128M -Xms128M -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=50 此時設(shè)置參數(shù)為這個

  • 在小堆空間時,cms有以下三個步驟:

    • CMS-initial-mark
    • CMS-concurrent-mark
    • Full gc(省略了remark,直接concurrent sweep)
  • 如果設(shè)置為1G的堆空間,再執(zhí)行一次,如下:

    [GC (Allocation Failure) [ParNew: 306688K->34048K(306688K), 0.1501462 secs] 655589K->460064K(1014528K), 0.1501758 secs] [Times: user=0.23 sys=0.04, real=0.15 secs] 
    [GC (CMS Initial Mark) [1 CMS-initial-mark: 426016K(707840K)] 465516K(1014528K), 0.0041295 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
    [CMS-concurrent-mark-start]
    clean map62070
    clean map64139
    [CMS-concurrent-mark: 0.052/0.052 secs] [Times: user=0.09 sys=0.01, real=0.05 secs] 
    [CMS-concurrent-preclean-start]
    [CMS-concurrent-preclean: 0.009/0.009 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
    [CMS-concurrent-abortable-preclean-start]
    clean map66208
    [GC (Allocation Failure) [ParNew: 306688K->34048K(306688K), 0.0219522 secs] 732704K->461842K(1014528K), 0.0219792 secs] [Times: user=0.04 sys=0.00, real=0.02 secs] 
    clean map68277
    [CMS-concurrent-abortable-preclean: 0.106/0.130 secs] [Times: user=0.21 sys=0.00, real=0.13 secs] 
    [GC (CMS Final Remark) [YG occupancy: 230336 K (306688 K)][Rescan (parallel) , 0.0225974 secs][weak refs processing, 0.0000146 secs][class unloading, 0.0004391 secs][scrub symbol table, 0.0004257 secs][scrub string table, 0.0001903 secs][1 CMS-remark: 427794K(707840K)] 658131K(1014528K), 0.0237353 secs] [Times: user=0.08 sys=0.00, real=0.02 secs] 
    [CMS-concurrent-sweep-start]
    clean map70346
    [GC (Allocation Failure) [ParNew: 306688K->34048K(306688K), 0.0415566 secs] 651750K->403148K(1014528K), 0.0415949 secs] [Times: user=0.11 sys=0.01, real=0.04 secs] 
    clean map72415
    clean map74484
    [GC (Allocation Failure) [ParNew: 306688K->34048K(306688K), 0.0723706 secs] 409986K->199788K(1014528K), 0.0724180 secs] [Times: user=0.19 sys=0.00, real=0.08 secs] 
    clean map76553
    [CMS-concurrent-sweep: 0.150/0.265 secs] [Times: user=0.58 sys=0.01, real=0.27 secs] 
    [CMS-concurrent-reset-start]
    [CMS-concurrent-reset: 0.001/0.001 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
    
    
    • 可以看到,這時候cms gc 需要以下幾個過程:

      • CMS-initial-mark 0.0041295 secs
      • CMS-concurrent-mark-start 共耗時 0.052+0.009+-0.130 = 0.191 secs
        • CMS-concurrent-mark 0.052 secs
        • CMS-concurrent-preclean-start
        • CMS-concurrent-preclean 0.009 secs
        • CMS-concurrent-abortable-preclean-start
        • CMS-concurrent-abortable-preclean 0.130 secs
      • CMS-Remark 0.0237353 secs
      • CMS-concurrent-sweep-start
      • CMS-concurrent-sweep 0.265 secs
      • CMS-concurrent-reset-start
      • CMS-concurrent-reset 0.001 secs

      五個大步驟,可以看到 共耗時:0.004 + 0.191 + 0.023 + 0.265 + 0.001 = 0.48 secs 其中兩個stw的階段:initial-mark 及remark均占用時間很短,而兩個concurrent階段則耗時較久。

    • 使用-Xmx128m -Xms128m -XX:+UseG1GC -XX:+PrintGCDetails

      由于G1 適用于大堆的情況,所以此時gc情況并不好:

      [GC pause (G1 Evacuation Pause) (young), 0.0153427 secs]
         [Parallel Time: 14.4 ms, GC Workers: 4]
            [GC Worker Start (ms): Min: 242.0, Avg: 242.8, Max: 243.5, Diff: 1.5]
            [Ext Root Scanning (ms): Min: 0.0, Avg: 0.9, Max: 2.7, Diff: 2.7, Sum: 3.8]
            [Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
               [Processed Buffers: Min: 0, Avg: 0.0, Max: 0, Diff: 0, Sum: 0]
            [Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
            [Code Root Scanning (ms): Min: 0.0, Avg: 0.2, Max: 0.8, Diff: 0.8, Sum: 0.8]
            [Object Copy (ms): Min: 10.4, Avg: 11.3, Max: 11.8, Diff: 1.4, Sum: 45.1]
            [Termination (ms): Min: 0.0, Avg: 0.6, Max: 0.8, Diff: 0.8, Sum: 2.3]
               [Termination Attempts: Min: 1, Avg: 1.0, Max: 1, Diff: 0, Sum: 4]
            [GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
            [GC Worker Total (ms): Min: 12.3, Avg: 13.0, Max: 13.7, Diff: 1.4, Sum: 52.0]
            [GC Worker End (ms): Min: 255.7, Avg: 255.8, Max: 255.9, Diff: 0.2]
         [Code Root Fixup: 0.0 ms]
         [Code Root Purge: 0.0 ms]
         [Clear CT: 0.1 ms]
         [Other: 0.8 ms]
            [Choose CSet: 0.0 ms]
            [Ref Proc: 0.7 ms]
            [Ref Enq: 0.0 ms]
            [Redirty Cards: 0.1 ms]
            [Humongous Register: 0.0 ms]
            [Humongous Reclaim: 0.0 ms]
            [Free CSet: 0.0 ms]
         [Eden: 14.0M(14.0M)->0.0B(8192.0K) Survivors: 0.0B->2048.0K Heap: 14.0M(128.0M)->12.5M(128.0M)]
       [Times: user=0.03 sys=0.01, real=0.02 secs] 
       
       ...... gc & fullgc
       
       [GC pause (G1 Evacuation Pause) (young) (to-space exhausted), 0.0054940 secs]
         [Parallel Time: 4.8 ms, GC Workers: 4]
            [GC Worker Start (ms): Min: 19175.3, Avg: 19175.4, Max: 19175.8, Diff: 0.6]
            [Ext Root Scanning (ms): Min: 0.0, Avg: 0.1, Max: 0.2, Diff: 0.2, Sum: 0.4]
            [Update RS (ms): Min: 1.4, Avg: 1.7, Max: 1.9, Diff: 0.5, Sum: 6.9]
               [Processed Buffers: Min: 14, Avg: 18.2, Max: 22, Diff: 8, Sum: 73]
            [Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
            [Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
            [Object Copy (ms): Min: 2.1, Avg: 2.5, Max: 2.8, Diff: 0.7, Sum: 9.9]
            [Termination (ms): Min: 0.0, Avg: 0.3, Max: 0.7, Diff: 0.7, Sum: 1.3]
               [Termination Attempts: Min: 1, Avg: 1.0, Max: 1, Diff: 0, Sum: 4]
            [GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
            [GC Worker Total (ms): Min: 4.2, Avg: 4.7, Max: 4.8, Diff: 0.6, Sum: 18.6]
            [GC Worker End (ms): Min: 19180.1, Avg: 19180.1, Max: 19180.1, Diff: 0.0]
         [Code Root Fixup: 0.0 ms]
         [Code Root Purge: 0.0 ms]
         [Clear CT: 0.0 ms]
         [Other: 0.6 ms]
            [Evacuation Failure: 0.4 ms]
            [Choose CSet: 0.0 ms]
            [Ref Proc: 0.1 ms]
            [Ref Enq: 0.0 ms]
            [Redirty Cards: 0.1 ms]
            [Humongous Register: 0.0 ms]
            [Humongous Reclaim: 0.0 ms]
            [Free CSet: 0.0 ms]
         [Eden: 0.0B(5120.0K)->0.0B(6144.0K) Survivors: 1024.0K->0.0B Heap: 126.1M(128.0M)->126.1M(128.0M)]
       [Times: user=0.01 sys=0.00, real=0.00 secs] 
      [Full GC (Allocation Failure)  126M->5897K(128M), 0.0209053 secs]
         [Eden: 0.0B(6144.0K)->0.0B(54.0M) Survivors: 0.0B->0.0B Heap: 126.1M(128.0M)->5897.8K(128.0M)], [Metaspace: 3317K->3317K(1056768K)]
       [Times: user=0.02 sys=0.01, real=0.02 secs] 
      [GC concurrent-mark-abort]
      spendTime:19005
      Heap
       garbage-first heap   total 131072K, used 41737K [0x00000007b8000000, 0x00000007b8100400, 0x00000007c0000000)
        region size 1024K, 36 young (36864K), 0 survivors (0K)
       Metaspace       used 3323K, capacity 4500K, committed 4864K, reserved 1056768K
        class space    used 366K, capacity 388K, committed 512K, reserved 1048576K
      

      說明:

      • 總耗時19005ms,是上面gc算法中最慢的
      • g1的gc日志跟之前的完全不同,增加了region的概念,每個region 1024K,也就是整個堆劃分為了128個region
      • young代有36個region,而且不存在survivors(from、to)
      • old代是剩余的92個region

      設(shè)置堆為1G,再看:

    [GC pause (G1 Evacuation Pause) (young), 0.0138758 secs]
       [Parallel Time: 12.6 ms, GC Workers: 4]
          [GC Worker Start (ms): Min: 5508.6, Avg: 5509.2, Max: 5510.8, Diff: 2.1]
          [Ext Root Scanning (ms): Min: 0.0, Avg: 0.1, Max: 0.2, Diff: 0.2, Sum: 0.5]
          [Update RS (ms): Min: 0.5, Avg: 1.9, Max: 2.5, Diff: 1.9, Sum: 7.4]
             [Processed Buffers: Min: 2, Avg: 4.8, Max: 8, Diff: 6, Sum: 19]
          [Scan RS (ms): Min: 0.0, Avg: 0.1, Max: 0.3, Diff: 0.3, Sum: 0.4]
          [Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
          [Object Copy (ms): Min: 9.8, Avg: 9.8, Max: 9.9, Diff: 0.1, Sum: 39.3]
          [Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
             [Termination Attempts: Min: 1, Avg: 1.5, Max: 2, Diff: 1, Sum: 6]
          [GC Worker Other (ms): Min: 0.0, Avg: 0.1, Max: 0.1, Diff: 0.0, Sum: 0.2]
          [GC Worker Total (ms): Min: 10.4, Avg: 12.0, Max: 12.5, Diff: 2.2, Sum: 47.9]
          [GC Worker End (ms): Min: 5521.2, Avg: 5521.2, Max: 5521.2, Diff: 0.0]
       [Code Root Fixup: 0.0 ms]
       [Code Root Purge: 0.0 ms]
       [Clear CT: 0.2 ms]
       [Other: 1.1 ms]
          [Choose CSet: 0.0 ms]
          [Ref Proc: 0.8 ms]
          [Ref Enq: 0.0 ms]
          [Redirty Cards: 0.0 ms]
          [Humongous Register: 0.0 ms]
          [Humongous Reclaim: 0.0 ms]
          [Free CSet: 0.2 ms]
       [Eden: 577.0M(577.0M)->0.0B(577.0M) Survivors: 37.0M->37.0M Heap: 774.5M(1024.0M)->197.5M(1024.0M)]
     [Times: user=0.05 sys=0.00, real=0.02 secs] 
    clean map93105
    clean map95174
    clean map97243
    clean map99312
    spendTime:5506
    Heap
     garbage-first heap   total 1048576K, used 676352K [0x0000000780000000, 0x0000000780102000, 0x00000007c0000000)
      region size 1024K, 501 young (513024K), 37 survivors (37888K)
     Metaspace       used 3324K, capacity 4500K, committed 4864K, reserved 1056768K
      class space    used 366K, capacity 388K, committed 512K, reserved 1048576K
    

    可以看到,共耗時5506ms,耗時降了很多。

    依然是1024k一個region,不過有501個是young代的region,其中37個是S區(qū)

上面例子的 GC 輸出可以看出,采用不同的垃圾回收機(jī)制及設(shè)定不同的線程數(shù),對于代碼段的整體執(zhí)行時間有較大的影響。需要根據(jù)情況有針對性地選用適合自己代碼段的垃圾回收機(jī)制。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容