JVM系列之GC

JVM系列之GC

談到JVM,你們都知道GC(Garbage Collection),GC這個話題說淺了就一句話--JVM自動垃圾收集,說深了就無止盡了,回收算法,各類收集器,gc類型,gc觸發點....等等,做者也是略懂皮毛,這裏給你們推薦一個知乎上比較活躍的JVM大牛,RednaxelaFX,是專門作JVM開發的,業界號稱"R大"。放個傳送門:R大
鑑於做者才學疏淺,這篇博文仍是準備用通熟易懂的話把做者本身對GC這一塊的理解作陳述,概要以下:html

文章結構算法

  1. 哪些內存須要回收(Which)
  2. 各類GC的觸發時機(When)
  3. 如何回收(How)
    3.1 回收算法
    3.2 HotSpot的具體實現-各類收集器
  4. GC日誌

1. 哪些內存須要回收(Which)

大多數沒幹過C或者C++的Javaer是幸福的,由於沒有體會過那種本身new delete內存的感受,建立對象就是new,無論內存的回收問題。其實咱們的內存是JVM的GC機制來幫咱們回收的。那麼問題來了。到底哪些內存須要回收呢?
答案:可達性分析算法,說白了,就是JVM預先肯定一組GC roots引用變量,如Student stu =new Student();這個stu就能夠做爲GC roots,當進行垃圾回收時,JVM經過GC Roots找到可以引用到的全部活對象,而後把剩下的對象標記爲"無用",即可回收狀態
可以做爲GC roots的引用以下:服務器

  • 全部Java線程當前活躍的棧幀裏指向GC堆裏的對象的引用;換句話說,當前 全部正在被調用的方法的引用類型的參數/局部變量/臨時值。
  • VM的一些靜態數據結構裏指向GC堆裏的對象的引用,例如說HotSpot VM裏的Universe裏有不少這樣的引用。
  • JNI handles,包括global handles和local handles(看狀況)全部當前被加載的Java類(看狀況)Java類的引用類型靜態變量(看狀況)Java類的運行時常量池裏的引用類型常量(String或Class類型)(看狀況)String常量池(StringTable)裏的引用

2. 各類GC的觸發時機(When)

2.1 GC類型

說到GC類型,就更有意思了,爲何呢,由於業界沒有統一的嚴格意義上的界限,也沒有嚴格意義上的GC類型,都是左邊一個教授一套名字,右邊一個做者一套名字。爲何會有這個狀況呢,由於GC類型是和收集器有關的,不一樣的收集器會有本身獨特的一些收集類型。因此做者在這裏引用R大關於GC類型的介紹,做者以爲仍是比較穩當準確的。以下:數據結構

  • Partial GC:並不收集整個GC堆的模式
    • Young GC(Minor GC):只收集young gen的GC
    • Old GC:只收集old gen的GC。只有CMS的concurrent collection是這個模式
    • Mixed GC:收集整個young gen以及部分old gen的GC。只有G1有這個模式
  • Full GC(Major GC):收集整個堆,包括young gen、old gen、perm gen(若是存在的話)等全部部分的模式。

2.2 觸發時機

上面你們也看到了,GC類型分分類是和收集器有關的,那麼固然了,對於不一樣的收集器,GC觸發時機也是不同的,做者就針對默認的serial GC來講:線程

  • young GC:當young gen中的eden區分配滿的時候觸發。注意young GC中有部分存活對象會晉升到old gen,因此young GC後old gen的佔用量一般會有所升高。
  • full GC:當準備要觸發一次young GC時,若是發現統計數聽說以前young GC的平均晉升大小比目前old gen剩餘的空間大,則不會觸發young GC而是轉爲觸發full GC(由於HotSpot VM的GC裏,除了CMS的concurrent collection以外,其它能收集old gen的GC都會同時收集整個GC堆,包括young gen,因此不須要事先觸發一次單獨的young GC);或者,若是有perm gen的話,要在perm gen分配空間但已經沒有足夠空間時,也要觸發一次full GC;或者System.gc()、heap dump帶GC,默認也是觸發full GC。

3. 如何回收(How)

3.1 回收算法

因爲網上已經擁有很是多的優秀博文來詳細介紹關於回收算法這塊,因此這塊做者將引用其餘博客的介紹並加上本身的一些描述:
3.1.1 標記清除算法(Mark-Sweep)
日誌


3.1.2複製算法(Coping)(絕大部分收集器的新生代使用的算法)

複製算法在JVM新生代垃圾回收中的運用:
cdn

Eden:From:TO =8:1:1
因爲新生代中90%的對象都是"朝生夕死",採用複製算法是比較合理的,首先只移動了存活下來的對象(比較少數),其次,內存在移動到To區域後是有順序的,不存在內存碎片。
值得一提的是,假如在一次MinorGC時,Eden中存活的對象+From中存活的對象>To的剩餘空間,則會經過擔保機制將對象直接轉移到Old gen ,若是Old gen的內存空間也不夠,則進行一次Full gc .
當對象的年齡到達15歲時會轉移到Old gen (可經過參數配置,通常不建議更改。)htm

3.1.3標記-整理算法(Mark-Compact):
對象

因爲Old gen 的大部分對象都是年齡很大的對象,因此存活率比較高,採用複製算法確定是行不通的(較多的對象複製操做),因此才大部分收集器的old gen採用 Mark-Compact算法,避免了空間碎片。blog

3.1.4三種算法比較:

稍微解釋一下常見的關於GC時間的問題:
爲何FGC的時間比MinorGC長不少?
答:FGC進行了old gen的gc,因爲算法上採用Mark-Sweep或者Mark-Compact,進行了不少對象(老年代存活率很低)的移動,固然很耗時了!其實就是空間換時間,時間換空間的問題。

3.2 HotSpot的具體實現-各類收集器

關於收集器這塊,因爲本人也是JVM初學者,加上不多有在生產環境作收集器參數調整,搭配使用的機會。因此能夠說對於一些HotSpot收集器只是停留在
書籍與博文層次,因此這裏就不賣弄了。下面給一個傳送門你們自行看一看吧:
www.jianshu.com/p/50d5c88b2…

4 GC日誌

-XX:+PrintGCDateStamps
-XX:+PrintGCDetails
-Xloggc:/Users/zdy/Desktop/dump/gclog.txt

當服務器出現卡頓比較頻繁時,嘗試看下本身的GC日誌,注意Full gc 頻率。

最後,稍微說一下做者的心得:

  • 若是是服務器一次卡頓時間比較長,通常是full gc時間過長,而應用最求的是卡頓(STW)時間短,能夠接受屢次卡頓,那麼能夠考慮調整加大young gen的比例,和提升進入老年代的年齡(默認15歲)來儘可能避免FGC.
  • 選擇合適的收集器很重要。要根據應用的特色。是追求吞吐量,仍是追求最小停頓。
  • 常常對照gc日誌觀察現實的狀況,如多長時間一卡頓,多久一卡頓,而後來調整本身的收集器或者相關的內存比例來達到本身想要的效果。
  • 在有限的物理資源條件下,要避免讓用戶接受過多的STW,能夠考慮在半夜自動進行gc(System.gc()),雖然不必定生效,但能夠觀察下效果。多數狀況下是會觸發full gc 的。
  • 大多數應用是能夠接受頻繁的mgc,但卻不能接受full gc 的長時間卡頓,因此在觀察gc日誌時必定要注意本身full gc的頻率和觸發條件(是因爲內存擔保,仍是年齡到了,仍是TO內存過小,致使每次都fgc.).
    因爲做者在gc這一塊也不是特別厲害,而且缺乏必定的實戰經驗,不敢妄自菲薄,因此給傳送門供你們參考閱讀:
    www.jianshu.com/p/088d71f20…
    www.cnblogs.com/mikevictor0…
    www.cnblogs.com/mikevictor0…
相關文章
相關標籤/搜索