圖解JVM的垃圾回收算法

不知不覺,JVM系列已經到回收算法的實現了。算法

本文主要內容服務器

 

先普及三個概念:多線程

並行收集:指多條垃圾收集線程並行工做,但此時用戶線程仍處於等待狀態。併發

併發收集:指用戶線程與垃圾收集線程同時工做(不必定是並行的可能會交替執行)。用戶程序在繼續運行,而垃圾收集程序運行在另外一個CPU上。佈局

吞吐量:即CPU用於運行用戶代碼的時間與CPU總消耗時間的比值(吞吐量 = 運行用戶代碼時間 / ( 運行用戶代碼時間 + 垃圾收集時間 ))。性能

例如:虛擬機共運行100分鐘,垃圾收集器花掉1分鐘,那麼吞吐量就是99%。優化

 

新生代

 

Serialspa

Serial收集器是最基本、發展歷史最悠久的收集器,曾經(在JDK1.3.1以前)是虛擬機新生代收集的惟一選擇。線程

它是一種單線程收集器,不只僅意味着它只會使用一個CPU或者一條收集線程去完成垃圾收集工做,更重要的是其在進行垃圾收集的時候須要暫停其餘線程。3d

優勢:簡單高效,擁有很高的單線程收集效率
缺點:收集過程須要暫停全部線程
算法:複製算法
應用:Client模式下的默認新生代收集器

收集過程:

ParNew

能夠把這個收集器理解爲Serial收集器的多線程版本。

優勢:在多CPU時,比Serial效率高。
缺點:收集過程暫停全部應用程序線程,單CPU時比Serial效率差。
算法:複製算法
應用:運行在Server模式下的虛擬機中首選的新生代收集器

收集過程:

Parallel Scanvenge

Parallel Scavenge收集器是一個新生代收集器,它也是使用複製算法的收集器,又是並行的多線程收集器,看上去和ParNew同樣,可是Parallel Scanvenge更關注系統的吞吐量 。

吞吐量 = 運行用戶代碼的時間 / (運行用戶代碼的時間 + 垃圾收集時間)

好比虛擬機總共運行了120秒,垃圾收集時間用了1秒,吞吐量=(120-1)/120=99.167%。

若吞吐量越大,意味着垃圾收集的時間越短,則用戶代碼能夠充分利用CPU資源,儘快完成程序的運算任務。

可設置參數:

-XX:MaxGCPauseMillis控制最大的垃圾收集停頓時間,
-XX:GC Time Ratio直接設置吞吐量的大小。

 

 

 

老年代

 

CMS=Concurrent Mark Sweep

特色:最短回收停頓時間,

回收算法:標記-清除

回收步驟:

  1. 初始標記:標記GC Roots直接關聯的對象,速度快
  2. 併發標記:GC Roots Tracing過程,耗時長,與用戶進程併發工做
  3. 從新標記:修正併發標記期間用戶進程運行而產生變化的標記,好似比初始標記長,可是遠遠小於併發標記
  4. 表發清除:清除標記的對象

缺點

對CPU資源很是敏感,CPU少於4個時,CMS對用戶程序的影響可能變得很大,由此虛擬機提供了「增量式併發收集器」;沒法回收浮動垃圾;採用標記清除算法會產生內存碎片,不過能夠經過參數開啓內存碎片的合併整理。

收集過程:

serial old

Serial Old收集器是Serial收集器的老年代版本,也是一個單線程收集器,不一樣的是採用"標記-整理算法",運行過程和Serial收集器同樣。

適用場景:

JDK1.5前與Parallel Scanvenge配合使用,做爲CMS的後備預案。

收集過程:

Parallel old

Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用多線程和"標記-整理算法"進行垃圾回收,吞吐量優先。

回收算法標記-整理

適用場景

爲了替代serial oldParallel Scanvenge配合使用。

收集過程:

G1=Garbage first

從 JDK 9 開始,JVM 的默認垃圾回收器就從 Parallel GC 調整爲 G1而且開始全面廢除 CMS 。

限制或者減小 GC 停頓時間相比系統吞吐量而言更加劇要,從 PGC 切換至低延遲的 G1 可以爲大部分用戶帶來更好的體驗。G1 的性能在 JDK 8 以及後續的 release 版本都獲得了極大的優化,G1 是一個具有全部 GC 特性的垃圾回收器,所以將 G1 設置爲 JVM 默認的 GC。

根據 JEP-291 中的說明,爲了減輕 GC 代碼的維護負擔以及加速新功能開發,決定在 JDK 9 中廢棄 CMS GC。

從 Java 9 開始,若是您使用 

-XX:+UseConcMarkSweepGC

(激活 CMS GC 算法的參數)參數啓動應用程序,則會在下面顯示警告消息:

Java HotSpot(TM) 64-Bit Server VM warning: Option UseConcMarkSweepGC was deprecated in version 9.0 and will likely be removed in a future release.

若是你想知道當前應用對應的 JVM 版本,你可使用如下命令進行查詢:

G1將整個JVM堆劃分紅多個大小相等的獨立區域regin,跟蹤各個regin裏面的垃圾堆積的價值大小,在後臺維護一個優先列表,每次根據容許的收集時間,優先回收最大的regin固然還保留有新生代和老年代的概念,但新生代和老年代不在是物理隔離了,他們都是一部分regin集合。

內存「化整爲零」的思路:GC根節點的枚舉範圍彙總加入remembered set 便可保證不對全堆掃面也不會遺漏。

回收步驟

  1. 初始標記:標記GC Roots直接關聯的對象
  2. 併發標記:對堆中對象進行可達性分析,找出存活對象,耗時長,與用戶進程併發工做
  3. 最終標記:修正併發標記期間用戶進程繼續運行而產生變化的標記
  4. 篩選回收:對各個regin的回收價值進行排序,而後根據指望的GC停頓時間制定回收計劃

G1收集器優點

並行與併發G1能充分利用多CPU、多核環境下的硬件優點,使用多個CPU來縮短Stop-The-World停頓時間。部分收集器本來須要停頓Java線程來執行GC動做,G1收集器仍然能夠經過併發的方式讓Java程序繼續運行。

分代收集G1可以獨自管理整個Java堆,而且採用不一樣的方式去處理新建立的對象和已經存活了一段時間、熬過屢次GC的舊對象以獲取更好的收集效果。

空間整合G1運做期間不會產生空間碎片,收集後能提供規整的可用內存。

可預測的停頓G1除了追求低停頓外,還能創建可預測的停頓時間模型。能讓使用者明確指定在一個長度爲M毫秒的時間段內,消耗在垃圾收集上的時間不得超過N毫秒。

收集過程:

G1的回收過程主要分爲 3 類:

(1)G1「年輕代」的垃圾回收,一樣叫 Minor G1,這個過程和咱們前面描述的相似,發生時機就是 Eden 區滿的時候。

(2)老年代的垃圾收集,嚴格上來講其實不算是收集,它是一個「併發標記」的過程,順便清理了一點點對象。

(3)真正的清理,發生在「混合模式」,它不止清理年輕代,還會將老年代的一部分區域進行清理。

 

ZGC

ZGC(Z Garbage Collector)是一款由Oracle公司研發的,以低延遲爲首要目標的一款垃圾收集器。它是基於動態Region內存佈局,(暫時)不設年齡分代,使用了讀屏障、染色指針和內存多重映射等技術來實現可併發的標記-整理算法的收集器。

JDK 11新加入,還在實驗階段,主要特色是:回收TB級內存(最大4T),停頓時間不超過10ms。

優勢低停頓,高吞吐量,ZGC收集過程當中額外耗費的內存小

缺點浮動垃圾

目前使用得很是少,真正普及仍是須要寫時間的。

 

垃圾收集器搭配關係

 

 

新生代收集器Serial、ParNewParallel Scavenge

老年代收集器CMS、Serial Old、Parallel Old

整堆收集器G1ZGC(由於不涉年代不在圖中)

 

 

如何選擇垃圾收集器?

 

前面咱們已經介紹完八種垃圾收集器。那麼我在真實場景中應該如何去選擇呢,下面給出幾種建議,但願對你有幫助:

  1. 若是你的堆大小不是很大(好比 100MB),選擇串行收集器通常是效率最高的。參數:-XX:+UseSerialGC
  2. 若是你的應用運行在單核的機器上,或者你的虛擬機核數只有單核,選擇串行收集器依然是合適的,這時候啓用一些並行收集器沒有任何收益。參數:-XX:+UseSerialGC
  3. 若是你的應用是「吞吐量」優先的,而且對較長時間的停頓沒有什麼特別的要求。選擇並行收集器是比較好的。參數:-XX:+UseParallelGC
  4. 若是你的應用對響應時間要求較高,想要較少的停頓。甚至 1 秒的停頓都會引發大量的請求失敗,那麼選擇G1ZGCCMS都是合理的。雖然這些收集器的 GC 停頓一般都比較短,但它須要一些額外的資源去處理這些工做,一般吞吐量會低一些。參數:-XX:+UseConcMarkSweepGC-XX:+UseG1GC-XX:+UseZGC 等。

從上面這些出發點來看,咱們日常的 Web 服務器,都是對響應性要求很是高的。選擇性其實就集中在 CMSG1ZGC上。而對於某些定時任務,使用並行收集器,是一個比較好的選擇。

 

總結

回顧文章前面的目錄

這幾點你學會了嗎?

相關文章
相關標籤/搜索