from:http://www.jianshu.com/p/2750c7c202ef

上周有幸給部門的小伙伴分享了一些JVM相關(guān)的知識,在整個做PPT的過程中,也是對一個領(lǐng)域的碎片知識的整理,本文將針對虛擬機GC相關(guān)的一些內(nèi)容進行整理,本文不會涉及到G1收集器。

在Hotspot VM實現(xiàn)中,主要有兩大類GC

  1. Partial GC:并不會堆整個GC堆進行收集
    • young gc:只收集 young gen 的GC
    • old gc:只收集 old gen 的GC,只有CMS的 concurrent collection
    • mixed GC:收集整個 young gen 以及部分 old gen 的GC,只有G1
  2. Full GC:收集整個堆,包括young gen、old gen、perm gen(如果存在的話)等

其實在各種文章或書上還可以看到Minor GC、Major GC的字眼,其中minor GC和young gc對應(yīng),而Major GC通常是和Full GC是等價的,由于HotSpot VM發(fā)展了這么多年,外界對各種名詞的解讀已經(jīng)完全混亂了,所以Major GC有時也可能是指old gc,在下定論之前一定要先問清楚。

單線程、并行、并發(fā)

在GC收集器實現(xiàn)中,分為了單線程、并行和并發(fā)。
單線程收集器:如 Serial GC,這個比較好理解,即垃圾收集過程中只有單一線程在進行收集工作,實現(xiàn)也最簡單。

并行收集器:如Parallel GC,每次運行時,不管是YGC,還是FGC,會 stop-the-world,暫停所有的用戶線程,并采用多個線程同時進行垃圾收集。

并發(fā)收集器:如CMS GC,在新生代進行垃圾收集時和并行收集器類似,都是并行收集(當(dāng)然具體算法中,你也可以設(shè)置成采用單線程進行收集),而且都會stop-the-world,主要的區(qū)別在于老年代的收集上,CMS在老年代進行垃圾收集時,大部分時間可以和用戶線程并發(fā)執(zhí)行的,只有小部分的時間stop-the-world,這就是它的優(yōu)勢,可以大大降低應(yīng)用的暫停時間,當(dāng)然也是有劣勢的。

算法組合

Hotspot VM實現(xiàn)的幾種GC算法組合中,其中CMS GC使用最廣,因為現(xiàn)在都是大內(nèi)存時代。

1、Serial GC

Serial generational collector (-XX:+UseSerialGC)
是全局范圍的Full GC,這種算法組合是最早出現(xiàn)的,當(dāng)年的Java堆內(nèi)存大小都還不大,使用Serial GC進行單線程收集,還感覺不出來GC耗時導(dǎo)致應(yīng)用暫停的問題

2、Parallel GC

Parallel for young space, serial for old space generational collector (-XX:+UseParallelGC).
Parallel for young and old space generational collector (-XX:+UseParallelOldGC)
當(dāng)Java堆慢慢變大時,發(fā)現(xiàn)已經(jīng)無法忍受GC耗時帶來的應(yīng)用暫停了,出現(xiàn)了Parallel GC,采用多線程的方式進行垃圾收集,很明顯可以提升垃圾收集效率。

3、CMS GC

Concurrent mark sweep with serial young space collector (-XX:+UseConcMarkSweepGC
–XX:-UseParNewGC)
Concurrent mark sweep with parallel young space collector (-XX:+UseConcMarkSweepGC)
當(dāng)Java堆達到更大時,比如8G,使用Parallel GC帶來的應(yīng)用暫停已經(jīng)很明顯了,所有又出現(xiàn)了 CMS GC,這是目前我看到線上環(huán)境使用的比較多的GC策略,在參數(shù)中添加-XX:+UseConcMarkSweepGC,對于 young gen,會自動選用 ParNewGC,不需要額外添加 -XX:+UseParNewGC

CMS雖然好,因為它的特殊算法,大部分的收集過程可以和用戶線程并發(fā)執(zhí)行,大大降低應(yīng)用的暫停時間,不過也會帶來負(fù)面影響,在收集完 old gen 之后,CMS并不會做整理過程,會產(chǎn)生空間碎片,如果這些碎片空間得不到利用,就會造成空間的浪費,整個過程中可能發(fā)生 concurrent mode failure,導(dǎo)致一次真正意義的 full gc,采用單線程對整個堆(young+old+perm) 使用MSC(Mark-Sweep-Compact)進行收集,這個過程意味著很慢很慢很慢,而且這個碎片問題是無法預(yù)測的.

4、G1 GC

G1 garbage collector (-XX:+UseG1GC),本文不對G1進行介紹

觸發(fā)條件

young gc

對于 young gc,觸發(fā)條件似乎要簡單很多,當(dāng) eden 區(qū)的內(nèi)存不夠時,就會觸發(fā)young gc,我們看看在 eden 區(qū)給對象分配一塊內(nèi)存是怎樣一個過程,畫了一個簡單的流程圖,我一直覺得一個好的示意圖可以讓一個枯燥的過程變得更有意思。

在 eden 區(qū)分配空間內(nèi)存不足時有兩種情況,為對象分配內(nèi)存、為TLAB分配內(nèi)存,總之就是內(nèi)存不夠,需要進行一次 young gc 為eden區(qū)騰出空間為后續(xù)的內(nèi)存申請做準(zhǔn)備,然后由一個用戶線程通知VM Thread,接下去要執(zhí)行一次 young gc。

full gc

1、old gen 空間不足

當(dāng)創(chuàng)建一個大對象、大數(shù)組時,eden 區(qū)不足以分配這么大的空間,會嘗試在old gen 中分配,如果這時 old gen 空間也不足時,會觸發(fā) full gc,為了避免上述導(dǎo)致的 full gc,調(diào)優(yōu)時應(yīng)盡量讓對象在 young gc 時就能夠被回收,還有不要創(chuàng)建過大的對象和數(shù)組。

2、統(tǒng)計得到的 young gc 晉升到 old gen的對象平均總大小大于old gen 的剩余空間

當(dāng)準(zhǔn)備觸發(fā)一次 young gc時,會判斷這次 young gc 是否安全,這里所謂的安全是當(dāng)前老年代的剩余空間可以容納之前 young gc 晉升對象的平均大小,或者可以容納 young gen 的全部對象,如果結(jié)果是不安全的,就不會執(zhí)行這次 young gc,轉(zhuǎn)而執(zhí)行一次 full gc

3、perm gen 空間不足

如果有perm gen的話,當(dāng)系統(tǒng)中要加載的類、反射的類和調(diào)用的方法較多,而且perm gen沒有足夠空間時,也會觸發(fā)一次 full gc

4、ygc出現(xiàn) promotion failure

promotion failure 發(fā)生在 young gc 階段,即 cms 的 ParNewGC,當(dāng)對象的gc年齡達到閾值時,或者 eden 的 to 區(qū)放不下時,會把該對象復(fù)制到 old gen,如果 old gen 空間不足時,會發(fā)生 promotion failure,并接下去觸發(fā)full gc

在GC日志中,有時會看到 concurrent mode failure 關(guān)鍵字,這是因為什么原因?qū)е碌膯栴}呢? 對這一塊的理解,很多文章都是說因為 concurrent mode failure 導(dǎo)致觸發(fā)full gc,其實應(yīng)該反過來,是full gc 導(dǎo)致的 concurrent mode failure,在cms gc的算法實現(xiàn)中,通常說的cms是由一個后臺線程定時觸發(fā)的,默認(rèn)每2秒檢查一次old gen的內(nèi)存使用率,當(dāng) old gen 的內(nèi)存使用率達到-XX:CMSInitiatingOccupancyFraction設(shè)置的值時,會觸發(fā)一次 cms gc,對 old gen 進行并發(fā)收集,而真正的 full gc 是通過 vm thread線程觸發(fā)的,而且在判斷當(dāng)前ygc會失敗的情況下觸發(fā)full gc,如上一次ygc出現(xiàn)了promotion failure,如果執(zhí)行 full gc 時,發(fā)現(xiàn)后臺線程正在執(zhí)行 cms gc,就會導(dǎo)致 concurrent mode failure。

對于以上這些情況,CMSInitiatingOccupancyFraction參數(shù)的設(shè)置就顯得尤為重要,設(shè)置的太大的話,發(fā)生CMS時的剩余空間太小,在ygc的時候容易發(fā)生promotion failure,導(dǎo)致 concurrent mode failure 發(fā)生的概率就增大,如果設(shè)置太小的話,會導(dǎo)致 cms gc 的頻率會增加,所以需要根據(jù)應(yīng)用的需求對該參數(shù)進行調(diào)優(yōu)。

5、執(zhí)行 System.gc()jmap -histo:live <pid>jmap -dump ...

參考資料
Major GC和Full GC的區(qū)別是什么?觸發(fā)條件呢

個人公眾號