讀《深入理解Java虛擬機》-垃圾收集器

Serial收集器

這是一個單線程收集器,在垃圾收集的時候會暫停其他的工作線程,美其名曰:Stop The World。停止其他線程的動作是jvm自動發起的,在用戶不知道的情況下中斷用戶的工作線程。
優點是簡單而高效 因爲他是單線程工作,專心收集垃圾,沒有線程的切換等開銷,對於運行在client模式下的虛擬機(新生代空間不會過大)來說,停頓100多毫秒內是可以接受的。
Serial收集器

ParNew收集器

ParNew收集器是Serial的多線程版本,沒有什麼創新之處。但是因爲只有它和Serial能和CMS收集器配合使用,所以它經常被用作Server模式下新生代的收集器。算是榜上一個大款。。。
參數:
-XX:+UseConcMarkSweepGC 默認新生代收集器
-XX:+UseParNewGC 強制指定作爲新生代收集器

ParNew收集器

Parallel Scavenge收集器

這個收集器同樣使用複製算法,也是多線程模式,看起來和上面的ParNew差不多,它關注的點是控制垃圾收集的程序的吞吐量。高吞吐量就可以高效率的利用CPU時間,儘快完成程序的運算任務。適合後臺運算而不需要太多交互的任務。
參數:
-XX:MaxGCPauseMillis 最大垃圾收集停頓時間
-XX:GCTimeRadio 設置吞吐量大小,取值(1,100)
-XX:UseAdaptiveSizePolicy 開關參數,打開後自動調節jvm各個內存區域大小和比例。(GC自適應調節策略)
MaxGCPauseMillis參數讓收集器儘可能的回收一次耗費的時間不超過設定值,但是如果把這個值設置的越小,雖然每次收集時間變短,但是收集的頻率會變高,所以是犧牲了吞吐量和新生代的空間。
GCTimeRadio參數是垃圾收集時間佔總時間的比例,相當於吞吐量的倒數,默認值是99,就是允許最大1%(即1/(1+99))的垃圾收集時間。如果是19,那就是1/(1+19)=5%,也就是垃圾收集時間最大佔5%的時間。

Serial Old收集器

這是Serial的老年代版本,同樣是單線程模式,使用標誌-整理算法,同樣給Client模式下的jvm用。要是在Server模式下,能夠與Parallel Scavenge收集器搭配使用,另外可以作爲CMS的備胎,在併發收集器發生Concurrent Mode failure時使用。
在這裏插入圖片描述

Parallel Old收集器

這是Parallel Scavenge的老年代版本,使用多線程和標誌-整理算法。與Parallel Scavenge搭配能夠提高CPU資源使用率和吞吐量。

Parallel Old收集器

CMS收集器

CMS收集器能夠使得回收停頓時間最短,一般在server模式下使用,採用標記-清楚算法,整個運作過程分爲4個步驟:

  1. 初始標記 標記一下GC Roots能直接關聯的對象,速度很快,需要stop the world。
  2. 併發標記 進行GC Roots Tracing的過程,追蹤引用鏈。因爲時間相對較長,所以沒有stop the world
  3. 重新標記 修正併發標記階段因用戶線程繼續工作而導致標記產生變動的那一部分對象的標記記錄。時間比初始標記稍長,但是遠短於併發標記。
  4. 併發清除 根據標記-清除算法回收被標記的對象。

缺點

  1. 對CPU資源非常敏感 在併發時,CMS收集器會佔有一部分CPU資源標記和清楚對象,那麼勢必會影響工作線程的,導致吞吐量下降。CMS默認啓動的線程數是(CPU數量+3)/4.
  2. 無法處理浮動垃圾 可能出現Concurrent Mode Failure失敗而導致另一次Full GC的產生。在CMS併發清理階段,工作線程還在持續的產生垃圾,這些垃圾就叫浮動垃圾。因爲是出現在標記之後,所以CMS肯定無法處理,只能留到下一次GC處理。在回收垃圾時還要預留一部分內存給工作線程用,所以不能等老年代填滿了才進行收集,可以通過參數-XX:CMSInitiatingOccupancyFraction的值來提高觸發百分比,以便降低迴收次數,提升性能。要是CMS在垃圾收集期間預留的內存無法滿足允許的線程需要,就會發生Concurrent Mode failure失敗,這時虛擬機將啓動備胎Serial Old,重新進行老年代的垃圾回收,這樣停頓的時間就有點久了,所以-XX:CMSInitiatingOccupancyFraction不能設置太高。
  3. 標記-清除算法產生內存碎片 會對大對象的分配帶來麻煩,明明老年代的空間還很多,但是就是沒有一個足夠大的連續空間來存儲新的大對象,這個時候不得不觸發一次full gc,爲了解決這個問題,CMS有個開關參數叫-XX:CMSCompactAtFullCollection,用於在馬上要進行full gc了去合併整理內存碎片,內存整理的過程是無法併發的,空間碎片被整理完,但是等待的時間也長了。所以還提供了一個參數叫 -XX:CMSFullGCsBeforeCompaction,這個參數用於設置執行多少次不壓縮的FullGC後,跟着來一次帶壓縮的。

CMS收集器

G1收集器

G1收集器把jvm的內存區域劃分爲一個一個獨立的region,雖然還保留着分代的概念,但是新生代和老年代不再是物理上的隔離,它們是多個region的集合。收集步驟基本和CMS類似:

  1. 初始標記 就是標記直接和GC Roots關聯的對象,需要停頓工作線程
  2. 併發標記 尋找GC Roots的引用鏈,也就是可達性分析,找存活對象。並且此時region裏面還可以創建新的對象,並且把對象的變化記錄到Remembered Set Logs裏面
  3. 最終標記 就是修正在併發標記階段狀態變化的對象,並把Remembered Set Logs合併到Remembered Set。需要停頓線程。
  4. 篩選回收 先對各個region的回收價值和成本排序,根據用戶期望的GC停頓時間來制定回收計劃。

特點:

  1. 並行與併發 主要就是利用多CPU和多核,縮短了stop the world時間
  2. 分代收集 使用region的方式來處理新創建和存活一段時間的,熬過多次GC的對象。
  3. 空間整合 G1從整體上看採用的是標記-整理算法,從局部看是複製算法,這兩種算法都是爲了讓內存更加規整,不容易產生內存碎片。
  4. 可預測的停頓 除了追求更短的停頓時間,G1收集器還可以指定在一個長度爲M毫秒內,消耗在gc的時間不超過N毫秒。

G1之所以能夠預測gc停頓的時間,是因爲它有計劃的避免在整個Java堆中進行全區域的垃圾收集。G1跟蹤各個region裏面垃圾堆積的價值大小(回收所獲的空間大小與回收所需時間的經驗值),在後臺維護一個優先列表,每次根據允許的收集時間來回收價值最大的 region。這樣使用region劃分空間以及按照價值優先級區域回收,保證了G1在有限的時間內回收效率儘可能高。
另外一個問題是在找GC Roots引用鏈的時候,如果一個region裏面的對象被其他region裏面的引用了,那麼是不是也要掃描所有的region,也就是全堆掃描?G1通過使用Remembered Set來避免全堆掃描,每個region都有一個對應的Remembered Set,虛擬機發現在對Reference類型的數據寫操作時,會產生一個Write Barrier暫時中斷寫操作,檢查Reference引用的對象是不是在另外一個region,如果是,就把引用信息記錄到被引用對象的所屬Region的Remembered Set。這樣在gc時,在根結點的枚舉範圍中加入Remembered Set就可以保證不進行全堆掃描也不會遺漏了。
G1收集器

總結
最後列出各個收集器的搭配使用關係
垃圾收集器搭配

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章