JVM垃圾收集—垃圾收集器及常见组合参数
鏈接: JVM垃圾收集—垃圾收集算法
上一篇介紹了垃圾收集算法及分區,這篇我們來學習垃圾收集器
文章目錄
- Serial
- ParNew
- Parallel Scavenge
- Serial Old
- Parallel Old
- CMS (Concurrent Mark Sweep)
- G1
- 理解吞吐量和停頓時間
- 如何選擇合適的垃圾收集器呢
首先我們要知道垃圾收集器有三種類型:
串行收集器 Serial 和 Serial Old
只能有一個垃圾回收線程執行,用戶線程暫停。(適用于內存較小的嵌入式設備)
并行收集器[吞吐量優先] Paraller Scanvenge、Parallel Old
多條垃圾收集線程并行工作,但此時用戶線程仍然處于等待階段。(適用于科學計算、后臺處理等若干交互場景)
并發收集器[停頓時間優先] CMS、G1
用戶線程和垃圾收集線程同時執行(但并不一定是并行的,可能是交替執行的),垃圾收集線程在執行的時候不會停頓用戶線程的運行。(適用于相對時間有要求的場景,比如WEB)
我按照發展順序給大家介紹一下:
Serial
- 復制算法
- 新生代
- 單線程收集器
特點:它只會使用一個CPU或者一條收集線程去完成垃圾收集工作,更重要的是其在垃圾收集的時候需要暫停其他線程。
優點:簡單高效
缺點:收集過程需要暫停所有線程。
應用:Client模式下的默認新生代收集器(Serial收集器是最基本、發展歷史最悠久的收集,之前(JDK1.3.1之前)是虛擬機新生代收集器的唯一選擇。)
ParNew
- 復制算法
- 新生代
- 多線程收集器
特點:ParNew收集器實質上是Serial收集器的多線程并行版本,除了同時使用多條線程進行垃圾收集之外,其余的行為包括Serial收集器可用的所有控制參數(例如:-XX:SurvivorRatio、-XX: PretenureSizeThreshold、-XX:HandlePromotionFailure等)、收集算法、Stop The World、對象分配規 則、回收策略等都與Serial收集器完全一致,在實現上這兩種收集器也共用了相當多的代碼。
優點:多CPU時,比Serial效率更高。
缺點:收集過程暫停所有的應用程序,單CPU核心時比Serial效率差。
應用:運行在Server模式下的虛擬機中首選的新生代收集器。
Parallel Scavenge
此收集器與吞吐量關系密切,故也稱為吞吐量優先收集器。
- 復制算法
- 新生代
- 多線程收集器
- 關注吞吐量
特點:多線程
Parallel Scavenge收集器使用兩個參數控制吞吐量:
控制最大的垃圾收集停頓時間 XX:MaxGCPauseMillis
直接設置吞吐量的大小 XX:GCTimeRatio
吞吐量 = 運行用戶代碼時間 / 運行用戶代碼時間 + 運行垃圾收集時間。
如果虛擬機完成某個任務,用戶代碼加上垃圾收集器總共耗時100分鐘,其中垃圾收集器花費了1分鐘,那吞吐量就是 99 / 100= 99%。
吞吐量越大,意味著垃圾收集的時間更短、則用戶代碼可以充分利用CPU資源,盡快完成程序的運算任務。
這里不是你設置了就一定有效,虛擬機會盡可能的靠近你設置的數值,并不是絕對一致
Serial Old
Serial Old 是Serial 收集器的老年代版本
- 標記整理
- 老年代
- 單線程收集器
Parallel Old
- 標記整理
- 老年代
- 多線程收集器
- 關注吞吐量
特點:多線程,采用標記-整理算法。
應用場景:注重高吞吐量以及CPU資源敏感的場合
CMS (Concurrent Mark Sweep)
是一種以獲取最短回收停頓時間為目標的收集器
- 標記清除
- 老年代
- 并發收集器
- 關注最短停頓時間
應用場景:適用于注重服務的響應速度,希望系統停頓時間最短,給用戶帶來良好的體驗。比如web服務,b/s結構。
工作分為四步:
第一步、初始標記(STW),標記GC Roots能直接關聯到的對象,速度非常快。
第二步、并發標記,進行GC Roots Tracing ,就是從GC Roots開始找到它能引用的所有對象的過程。
第三步、重新標記(STW),為了修成并發標記期間因用戶程序繼續運作導致標記產生變動的一部分對象的標記記錄。這個階段的停頓時間一般會比初始標記階段稍長一些,但遠比并發標記的時間要短。
第四步、并發清除,在整個過程中耗時最長的并發標記和并發清除過程,收集器線程都可以與用戶線程一起工作,因此,從總體上看,CMS收集器的內存回收過程與用戶線程一起并發執行的
優點:并發收集、并發清除、低停頓。
缺點:對CPU要求高,無法處理浮動垃圾、產生大量空間碎片、并發階段會降低吞吐量。
這里有一個刁鉆的面試問題:
CMS默認晉升老年代為6的原因: 簡單來說,CMS對內存尤其敏感,且會導致單線程Serial FullGC 這個是非常嚴重的后果,而從結果上說越大的MaxTenuringThreshold會更快的導致heap的碎片化(不光old 區,首先要明白對于內存的分配并不是真的一個對象一個對象緊密排列的),所以歷代CMS 默認這個值都會比較小(JDK8以前是4,之后調整為6)
G1
- 分代收集(仍然保留分代的概念)
- 并行與并發
- 老年代和新生代
- 關注最短停頓時間
- 內存分為Region[?ri?d??n] 區(內存是否連續)
- 可以設置最短停頓時間
工作也是分為四步:
第一步、初始標記(STW),標記GC Roots能直接關聯到的對象(速度很快)。
第二步、并發標記,進行 GC Roots Tracing,就是從GC Roots開始找到它能引用的所有其他對象的過程。
第三步、最終標記(STW),為了修正并發標記期間因用戶程序繼續運作而導致標記產生變動的那一部分的標記記錄。這個階段的停頓時間一般會比初始標記階段稍微長,但是要比并發標記要短。
第四步、篩選回收(STW),對各個Region的回收價值和成本進行排序,根據用戶所期望的GC停頓時間指定回收計劃。
G1 總結:
JDK 7 開始使用,JDK8非常成熟,JDK9默認的垃圾收集器。
如果停頓時間過短,會造成頻繁垃圾回收,會導致OOM:GC overhead limitexceeded (超過98%的時間用來做GC并且回收了不到2%的堆內存時會拋出此異常)
虛擬機怎么判斷是否需要使用G1收集器?
答:50%以上的堆被存活對象占用、對象分配和晉升的速度變化非常大、垃圾回收時間較長。
理解吞吐量和停頓時間
停頓時間 = 垃圾收集器進行垃圾回收的執行時間
吞吐量 = 運行用戶代碼時間 / 運行用戶代碼時間 + 運行垃圾收集時間。
如果虛擬機完成某個任務,用戶代碼加上垃圾收集器總共耗時100分鐘,其中垃圾收集器花費了1分鐘,那吞吐量就是 99 / 100= 99%。
吞吐量越大,意味著垃圾收集的時間更短、則用戶代碼可以充分利用CPU資源,盡快完成程序的運算任務。
停頓時間越短就越適合需要和用戶交互的程序,良好的響應速度能提升用戶體驗; 高吞吐量則可以高效地利用CPU時間,盡快完成程序的運算任務,主要適合在后臺運算而不 需要太多交互的任務。
吞吐量和停頓時間是衡量垃圾回收器的標準,我們進行調優也是觀察這兩個變量。
如何選擇合適的垃圾收集器呢
這個準則只能參考,因為性能取決于堆的大小,應用程序維護的實時數據量以及可用處理器的數量和速度。
大白話:牛逼哄哄的硬件設備不怎么需要調優,垃圾設備才考驗你的調優技能。
如果應用程序的內存在100M左右,使用串行收集器 -XX:+UseSerialGC。
如果是單核心,并且沒有停頓要求,默認收集器,或者選擇帶有選項的-XX:+UseSerialGC
如果允許停頓時間超過1秒或者更長時間,默認收集器,或者選擇并行-XX:+UseParallelGC
如果響應時間最重要,并且不能超過1秒,使用并發收集器 -XX:+UseConcMarkSweepGC or -XX:+UseG1GC
1.8默認的垃圾回收:PS + ParallelOld
- -XX:+UseSerialGC = Serial New (DefNew) + Serial Old
小型程序。默認情況下不會是這種選項,HotSpot會根據計算及配置和JDK版本自動選擇收集器 - -XX:+UseParNewGC = ParNew + SerialOld
這個組合已經很少用(在某些版本中已經廢棄)
為什么廢棄的官方解釋:
鏈接: Why Remove support for ParNew+SerialOld and DefNew+CMS in the future? - -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
總結
以上是生活随笔為你收集整理的JVM垃圾收集—垃圾收集器及常见组合参数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2022年Google SEO基础知识,
- 下一篇: 零售行业陈列管理系统