如今常見的垃圾收集器有以下幾種:算法
新生代收集器:安全
老年代收集器:性能優化
堆內存垃圾收集器:G1服務器
每種垃圾收集器之間有連線,表示他們能夠搭配使用。markdown
(1)Serial 收集器多線程
Serial 是一款用於新生代的單線程收集器,採用複製算法進行垃圾收集。Serial 進行垃圾收集時,不只只用一條線程執行垃圾收集工做,它在收集的同時,全部的用戶線程必須暫停(Stop The World)。併發
就好比媽媽在家打掃衛生的時候,確定不會邊打掃邊讓兒子往地上亂扔紙屑,不然一邊製造垃圾,一遍清理垃圾,這活啥時候也幹不完。post
以下是 Serial 收集器和 Serial Old 收集器結合進行垃圾收集的示意圖,當用戶線程都執行到安全點時,全部線程暫停執行,Serial 收集器以單線程,採用複製算法進行垃圾收集工做,收集完以後,用戶線程繼續開始執行。性能
適用場景:Client 模式(桌面應用);單核服務器。測試
能夠用 -XX:+UserSerialGC 來選擇 Serial 做爲新生代收集器。
(2)ParNew 收集器
ParNew 就是一個 Serial 的多線程版本,其它與Serial並沒有區別。ParNew 在單核 CPU 環境並不會比 Serial 收集器達到更好的效果,它默認開啓的收集線程數和 CPU 數量一致,能夠經過 -XX:ParallelGCThreads 來設置垃圾收集的線程數。
以下是 ParNew 收集器和 Serial Old 收集器結合進行垃圾收集的示意圖,當用戶線程都執行到安全點時,全部線程暫停執行,ParNew 收集器以多線程,採用複製算法進行垃圾收集工做,收集完以後,用戶線程繼續開始執行。
適用場景:多核服務器;與 CMS 收集器搭配使用。當使用 -XX:+UserConcMarkSweepGC 來選擇 CMS 做爲老年代收集器時,新生代收集器默認就是 ParNew,也能夠用 -XX:+UseParNewGC 來指定使用 ParNew 做爲新生代收集器。
(3)Parallel Scavenge 收集器
Parallel Scavenge 也是一款用於新生代的多線程收集器,與 ParNew 的不一樣之處是ParNew 的目標是儘量縮短垃圾收集時用戶線程的停頓時間,Parallel Scavenge 的目標是達到一個可控制的吞吐量。
吞吐量就是 CPU 執行用戶線程的的時間與 CPU 執行總時間的比值【吞吐量 = 運行用戶代代碼時間/(運行用戶代碼時間+垃圾收集時間)】,好比虛擬機一共運行了 100 分鐘,其中垃圾收集花費了 1 分鐘,那吞吐量就是 99% 。好比下面兩個場景,垃圾收集器每 100 秒收集一次,每次停頓 10 秒,和垃圾收集器每 50 秒收集一次,每次停頓時間 7 秒,雖而後者每次停頓時間變短了,可是整體吞吐量變低了,CPU 整體利用率變低了。
能夠經過 -XX:MaxGCPauseMillis 來設置收集器儘量在多長時間內完成內存回收,能夠經過 -XX:GCTimeRatio 來精確控制吞吐量。
以下是 Parallel 收集器和 Parallel Old 收集器結合進行垃圾收集的示意圖,在新生代,當用戶線程都執行到安全點時,全部線程暫停執行,ParNew 收集器以多線程,採用複製算法進行垃圾收集工做,收集完以後,用戶線程繼續開始執行;在老年代,當用戶線程都執行到安全點時,全部線程暫停執行,Parallel Old 收集器以多線程,採用標記整理算法進行垃圾收集工做。
適用場景:注重吞吐量,高效利用 CPU,須要高效運算且不須要太多交互。
可使用 -XX:+UseParallelGC 來選擇 Parallel Scavenge 做爲新生代收集器,jdk七、jdk8 默認使用 Parallel Scavenge 做爲新生代收集器。
(1)Serial Old 收集器
Serial Old 收集器是 Serial 的老年代版本,一樣是一個單線程收集器,採用標記-整理算法。
以下圖是 Serial 收集器和 Serial Old 收集器結合進行垃圾收集的示意圖:
適用場景:Client 模式(桌面應用);單核服務器;與 Parallel Scavenge 收集器搭配;做爲 CMS 收集器的後備預案。
(2)CMS(Concurrent Mark Sweep) 收集器
CMS 收集器是一種以最短回收停頓時間爲目標的收集器,以 「 最短用戶線程停頓時間 」 著稱。整個垃圾收集過程分爲 4 個步驟:
① 初始標記:標記一下 GC Roots 能直接關聯到的對象,速度較快。
② 併發標記:進行 GC Roots Tracing,標記出所有的垃圾對象,耗時較長。
③ 從新標記:修正併發標記階段引用戶程序繼續運行而致使變化的對象的標記記錄,耗時較短。
④ 併發清除:用標記-清除算法清除垃圾對象,耗時較長。
整個過程耗時最長的併發標記和併發清除都是和用戶線程一塊兒工做,因此從整體上來講,CMS 收集器垃圾收集能夠看作是和用戶線程併發執行的。
CMS 收集器也存在一些缺點:
對 CPU 資源敏感:默認分配的垃圾收集線程數爲(CPU 數+3)/4,隨着 CPU 數量降低,佔用 CPU 資源越多,吞吐量越小
沒法處理浮動垃圾:在併發清理階段,因爲用戶線程還在運行,還會不斷產生新的垃圾,CMS 收集器沒法在當次收集中清除這部分垃圾。同時因爲在垃圾收集階段用戶線程也在併發執行,CMS 收集器不能像其餘收集器那樣等老年代被填滿時再進行收集,須要預留一部分空間提供用戶線程運行使用。當 CMS 運行時,預留的內存空間沒法知足用戶線程的須要,就會出現 「 Concurrent Mode Failure 」的錯誤,這時將會啓動後備預案,臨時用 Serial Old 來從新進行老年代的垃圾收集。
由於 CMS 是基於標記-清除算法,因此垃圾回收後會產生空間碎片,能夠經過 -XX:UserCMSCompactAtFullCollection 開啓碎片整理(默認開啓),在 CMS 進行 Full GC 以前,會進行內存碎片的整理。還能夠用 -XX:CMSFullGCsBeforeCompaction 設置執行多少次不壓縮(不進行碎片整理)的 Full GC 以後,跟着來一次帶壓縮(碎片整理)的 Full GC。
適用場景:重視服務器響應速度,要求系統停頓時間最短。可使用 -XX:+UserConMarkSweepGC 來選擇 CMS 做爲老年代收集器。
(3)Parallel Old 收集器
Parallel Old 收集器是 Parallel Scavenge 的老年代版本,是一個多線程收集器,採用標記-整理算法。能夠與 Parallel Scavenge 收集器搭配,能夠充分利用多核 CPU 的計算能力。
適用場景:與Parallel Scavenge 收集器搭配使用;注重吞吐量。jdk七、jdk8 默認使用該收集器做爲老年代收集器,使用 -XX:+UseParallelOldGC 來指定使用 Paralle Old 收集器。
G1 收集器
G1 收集器是 jdk1.7 才正式引用的商用收集器,如今已經成爲 jdk9 默認的收集器。前面幾款收集器收集的範圍都是新生代或者老年代,G1 進行垃圾收集的範圍是整個堆內存,它採用 「 化整爲零 」 的思路,把整個堆內存劃分爲多個大小相等的獨立區域(Region),在 G1 收集器中還保留着新生代和老年代的概念,它們分別都是一部分 Region,以下圖:
每個方塊就是一個區域,每一個區域多是 Eden、Survivor、老年代,每種區域的數量也不必定。JVM 啓動時會自動設置每一個區域的大小(1M ~ 32M,必須是 2 的次冪),最多能夠設置 2048 個區域(即支持的最大堆內存爲 32M*2048 = 64G),假如設置 -Xmx8g -Xms8g,則每一個區域大小爲 8g/2048=4M。
爲了在 GC Roots Tracing 的時候避免掃描全堆,在每一個 Region 中,都有一個 Remembered Set 來實時記錄該區域內的引用類型數據與其餘區域數據的引用關係(在前面的幾款分代收集中,新生代、老年代中也有一個 Remembered Set 來實時記錄與其餘區域的引用關係),在標記時直接參考這些引用關係就能夠知道這些對象是否應該被清除,而不用掃描全堆的數據。
G1 收集器能夠 「 創建可預測的停頓時間模型 」,它維護了一個列表用於記錄每一個 Region 回收的價值大小(回收後得到的空間大小以及回收所需時間的經驗值),這樣能夠保證 G1 收集器在有限的時間內能夠得到最大的回收效率。
以下圖所示,G1 收集器收集器收集過程有初始標記、併發標記、最終標記、篩選回收,和 CMS 收集器前幾步的收集過程很類似:
① 初始標記:標記出 GC Roots 直接關聯的對象,這個階段速度較快,須要中止用戶線程,單線程執行。
② 併發標記:從 GC Root 開始對堆中的對象進行可達新分析,找出存活對象,這個階段耗時較長,但能夠和用戶線程併發執行。
③ 最終標記:修正在併發標記階段引用戶程序執行而產生變更的標記記錄。
④ 篩選回收:篩選回收階段會對各個 Region 的回收價值和成本進行排序,根據用戶所指望的 GC 停頓時間來指定回收計劃(用最少的時間來回收包含垃圾最多的區域,這就是 Garbage First 的由來——第一時間清理垃圾最多的區塊),這裏爲了提升回收效率,並無採用和用戶線程併發執行的方式,而是停頓用戶線程。
適用場景:要求儘量可控 GC 停頓時間;內存佔用較大的應用。能夠用 -XX:+UseG1GC 使用 G1 收集器,jdk9 默認使用 G1 收集器。
本文主要介紹了JVM中的垃圾回收器,主要包括串行回收器、並行回收器以及CMS回收器、G1回收器。他們各自都有優缺點,一般來講你須要根據你的業務,進行基於垃圾回收器的性能測試,而後再作選擇。下面給出配置回收器時,常用的參數:
-XX:+UseSerialGC:在新生代和老年代使用串行收集器
-XX:+UseParNewGC:在新生代使用並行收集器
-XX:+UseParallelGC :新生代使用並行回收收集器,更加關注吞吐量
-XX:+UseParallelOldGC:老年代使用並行回收收集器
-XX:ParallelGCThreads:設置用於垃圾回收的線程數
-XX:+UseConcMarkSweepGC:新生代使用並行收集器,老年代使用CMS+串行收集器
-XX:ParallelCMSThreads:設定CMS的線程數量
-XX:+UseG1GC:啓用G1垃圾回收器
後續會持續更新性能優化專題知識,寫的很差的地方也但願大牛能指點一下,你們以爲不錯能夠點個贊在關注下,之後還會分享更多文章!