如何選擇JVM垃圾回收器?

明確垃圾回收器組合

-XX:+UseSerialGC 年輕代和老年代都用串行收集器
-XX:+UseParNewGC 年輕代使用ParNew,老年代使用 Serial Old
-XX:+UseParallelGC 年輕代使用Paraller Scavenge,老年代使用Serial Old
-XX:+UseParallelOldGC 新生代Paraller Scavenge,老年代使用Paraller Old
-XX:+UseConcMarkSweepGC,表示年輕代使用ParNew,老年代的用CMS + Serial Old
-XX:+UseG1GC 使用G1垃圾回收器
-XX:+UseZGC 使用ZGC垃圾回收器

垃圾收集器跟內存大小的大致關係(不精確,需要根據實際環境測試)

  1. Serial:幾十兆
  2. PS:上百兆 ~ 4G
  3. CMS:4G ~ 10G
  4. G1:10G~上百G
  5. ZGC: 4T – 16T(JDK13)

常見的垃圾回收器

  1. JDK誕生 Serial追隨 提高效率,誕生了PS,為了配合CMS,誕生了PN,CMS是1.4版本後期引入,CMS是里程碑式的GC,它開啟了並發回收的過程,但是CMS毛病較多,因此目前任何一個JDK版本默認是CMS 並發垃圾回收是因為無法忍受STW
  2. Serial 年輕代 串行回收
  3. PS 年輕代 並行回收
  4. ParNew 年輕代 配合CMS的並行回收
  5. SerialOld
  6. ParallelOld
  7. ConcurrentMarkSweep 老年代 並發的, 垃圾回收和應用程序同時運行,降低STW的時間(200ms) CMS問題比較多,所以現在沒有一個版本默認是CMS,只能手工指定 CMS既然是MarkSweep,就一定會有碎片化的問題,碎片到達一定程度,CMS的老年代分配對象分配不下的時候,使用SerialOld 進行老年代回收 想像一下:PS + PO -> 加內存 換垃圾回收器 -> PN + CMS + SerialOld(幾個小時 – 幾天的STW) 幾十個G的內存,單線程回收 -> G1 + FGC 幾十個G -> 上T內存的服務器 ZGC 算法:三色標記 + Incremental Update
  8. G1(10ms) 算法:三色標記 + SATB
  9. ZGC (1ms) PK C++ 算法:ColoredPointers + LoadBarrier
  10. Shenandoah 算法:ColoredPointers + WriteBarrier
  11. Eplison

常見垃圾回收器組合參數設定:(1.8)

  • -XX:+UseSerialGC = Serial New (DefNew) + Serial Old
    小型程序。默認情況下不會是這種選項,HotSpot會根據計算及配置和JDK版本自動選擇收集器

  • -XX:+UseParNewGC = ParNew + SerialOld這個組合已經很少用(在某些版本中已經廢棄)

  • -XX:+UseConc(urrent)MarkSweepGC = ParNew + CMS + Serial Old

  • -XX:+UseParallelGC = Parallel Scavenge + Parallel Old (1.8默認) 【PS + SerialOld】

  • -XX:+UseParallelOldGC = Parallel Scavenge + Parallel Old

  • -XX:+UseG1GC = G1

  • 默認GC的查看方法
    1.java -XX:+PrintCommandLineFlags -version
    2.通過GC的日誌來分辨

  • Linux下1.8版本默認的垃圾回收器到底是什麼?

    • 1.8.0_181 默認(看不出來)Copy MarkCompact
    • 1.8.0_222 默認 PS + PO

面對如此眾多的垃圾回收器,我們應該如何選擇一款適合自己應用的收集器呢?這個問題的答案主要受以下三個因素影響:

應用程序的主要關注點是什麼?

  1. 數據分析、科學計算類的任務,目標是能儘快算出結果,那吞吐量就是主要關注點;
  2. SLA 應用,那停頓時間直接影響服務質量,嚴重的甚至會導致事務超時,這樣延遲就是主要關注點;
  3. 客戶端應用或者嵌入式應用,那垃圾收集的內存佔用則是不可忽視的。

運行應用的基礎設施如何?

譬如硬件規格,要涉及的系統架構是 x86-32/64、SPARC 還是ARM/Aarch64;處理器的數量多少,分配內存的大小;選擇的操作系統是 Linux、Solaris 還是 Windows等。

使用 JDK 的發行商是什麼?

版本號是多少?是 ZingJDK/Zulu、OracleJDK、Open-JDK、OpenJ9 抑或是其他公司的發行版?該 JDK 對應了《Java 虛擬機規範》的哪個版本?

舉例說明

一般來說,收集器的選擇就從以上這幾點出發來考慮。
舉個例子,假設某個直接面向用戶提供服務的 B/S 系統準備選擇垃圾收集器,一般來說延遲時間是這類應用的主要關注點,那麼:

  1. 如果你有充足的預算但沒有太多調優經驗,那麼一套帶商業技術支持的專有硬件或者軟件解決方案是不錯的選擇,Azul 公司以前主推的 Vega 系統和現在主推的 Zing VM 是這方面的代表,這樣你就可以使用傳說中的 C4 收集器了。
  2. 如果你雖然沒有足夠預算去使用商業解決方案,但能夠掌控軟硬件型號,使用較新的版本,同時又特別注重延遲,那 ZGC 很值得嘗試。
  3. 如果你對還處於實驗狀態的收集器的穩定性有所顧慮,或者應用必須運行在 Win-dows 操作系統下,那 ZGC 就無緣了,試試 Shenandoah 吧
  4. 如果你接手的是遺留系統,軟硬件基礎設施和 JDK 版本都比較落後,那就根據內存規模衡量一下,對於大概 10G 以下的堆內存,CMS 一般能處理得比較好,而對於更大的堆內存,可重點考察一下 G1。(這個一般現階段使用較多)

當然,以上都是僅從理論出發的分析,實戰中切不可紙上談兵,根據系統實際情況去測試才是選擇收集器的最終依據。

Tags: