前面文章中,咱們介紹了 Java 虛擬機的內存結構,Java 虛擬機的垃圾回收機制,那麼這篇文章咱們說說具體執行垃圾回收的垃圾回收器。html
總的來講,Java 虛擬機的垃圾回收器能夠分爲四大類別:串行回收器、並行回收器、CMS 回收器、G1 回收器。java
串行回收器是指使用單線程進行垃圾回收的回收器。由於每次回收時只有一個線程,所以串行回收器在併發能力較弱的計算機上,其專一性和獨佔性的特色每每能讓其有更好的性能表現。算法
串行回收器能夠在新生代和老年代使用,根據做用於不一樣的堆空間,分爲新生代串行回收器和老年代串行回收器。多線程
新生代串行回收器併發
串行收集器是全部垃圾回收器中最古老的一種,也是 JDK 中最基本的垃圾回收器之一。jvm
在新生代串行回收器中使用的是複製算法。在串行回收器進行垃圾回收時,會觸發 Stop-The-World 現象,即其餘線程都須要暫停,等待垃圾回收完成。所以在某些狀況下,其會形成較爲糟糕的用戶體驗。性能
使用 -XX:+UseSerialGC
參數能夠指定使用新生代串行收集器和老年代串行收集器。當虛擬機在 Client 模式下運行時,其默認使用該垃圾收集器。學習
老年代串行回收器spa
在老年代串行回收器中使用的是標記壓縮算法。其與新生代串行收集器同樣,只能串行、獨佔式地進行垃圾回收,所以也常常會有較長時間的 Stop-The-World 發生。線程
但老年代串行回收器的好處之一,就是其能夠與多種新生代回收器配合使用。若要啓用老年代串行回收器,能夠嘗試如下參數:
-XX:UseSerialGC
:新生代、老年代都使用串行回收器。-XX:UseParNewGC
:新生代使用 ParNew 回收器,老年代使用串行回收器。-XX:UseParallelGC
:新生代使用 ParallelGC 回收器,老年代使用串行回收器。並行回收器在串行回收器的基礎上作了改進,其使用多線程進行垃圾回收。對於並行能力強的機器,能夠有效縮短垃圾回收所使用的時間。
根據做用內存區域的不一樣,並行回收器也有三個不一樣的回收器:新生代 ParNew 回收器、新生代 ParallelGC 回收器、老年代 ParallelGC 回收器。
新生代 ParNew 回收器
新生代 ParNew 回收器工做在新生代,其只是簡單地將串行回收器多線程化,其回收策略、算法以及參數和新生代串行回收器同樣。
新生代 ParNew 回收器一樣使用複製的垃圾回收算法,其垃圾收集過程當中一樣會觸發 Stop-The-World 現象。但由於其使用多線程進行垃圾回收,所以在併發能力強的 CPU 上,其產生的停頓時間要短於串行回收器。
但在單 CPU 或並能能力弱的系統中,並行回收器效果會由於線程切換的緣由,其實際表現反而不如串行回收器。
要開啓新生代 ParNew 回收器,可使用如下參數:
-XX:+UseParNewGC
:新生代使用 ParNew 回收器,老年代使用串行回收器。-XX:UseConcMarkSweepGC
:新生代使用 ParNew 回收器,老年代使用 CMS。-XX:ParallelGCThreads
:指定 ParNew 回收器的工做線程數量。新生代 Parallel GC 回收器
新生代 Parallel GC 回收器與新生代 ParNew 回收器很是相似,其也是使用複製算法,都是多線程、獨佔式的收集器,也會致使 Stop-The-World。但其他 ParNew 回收器的一個重大不一樣是:其很是注重系統的吞吐量。
之因此說新生代 Parallel GC 回收器很是注重系統吞吐量,是由於其有一個自適應 GC 調節策略。咱們可使用 -XX:+UseAdaptiveSizePolicy
參數打開這個策略,在這個模式下,新生代的大小、Eden 和 Survivor 的比例、晉升老年代的對象年齡等參數都會被自動調節,已達到堆大小、吞吐量、停頓時間的平衡點。
Parallel GC 回收器提供了兩個重要參數用於控制系統的吞吐量。
-XX:MaxGCPauseMillis
:設置最大垃圾收集停頓時間。在 ParallelGC 工做時,其會自動調整響應參數,將停頓時間控制在設置範圍內。爲了達到目的,其可能會使用較小的堆,但這會致使 GC 較爲頻繁。-XX:GCTimeRatio
:設置吞吐量大小,其實一個 0 - 100 的整數。假設 GCTimeRatio 的值爲 n,那麼系統將不花費超過 1/(1+n) 的時間用於垃圾手機。好比 GCTimeRatio 值爲 19,那麼系統用於垃圾收集的時間不超過 1 /(1+19) = 5%。默認狀況下,它的取值是 99,即不超過 1% 的時間用於垃圾收集。新生代 Parallel GC 回收器可使用如下參數啓用:
-XX:+UseParallelGC
:新生代使用 Parallel 回收器,老年代使用串行回收器。-XX:+UseParallelOldGC
:新生代使用 ParallelGC 回收器,老年代使用 ParallelOldGC 回收器。老年代 ParallelOldGC 回收器
老年代 ParallelOldGC 回收器也是一種多線程併發的回收器,與新生代 ParallelGC 收集器同樣,其也是注重吞吐量的收集器,只不過其是做用於老年代。
ParallelOldGC 回收器使用的是標記壓縮算法,只有在 JDK 1.6 中才可使用。咱們可使用-XX:UseParallelOldGC
參數在新生代中使用 ParallelGC 收集器,在老年代中使用 ParallelOldGC 收集器。參數 -XX:ParallelGCThreads
也能夠用於設置垃圾回收時的線程數量。
與 ParallelGC 和 ParallelOldGC 不一樣,CMS 回收器主要關注系統停頓時間。CMS 回收器全稱爲 Concurrent Mark Sweep,意爲標記清除算法,其是一個使用多線程並行回收的垃圾回收器。
工做步驟
CMS 的主要工做步驟有:初始標記、併發標記、預清理、從新標記、併發清除和併發充值。其中初始標記和從新標記是獨佔系統資源的,而其餘階段則能夠和用戶線程一塊兒執行。
在整個 CMS 回收過程當中,默認狀況下會有預清理的操做,咱們能夠關閉開關 -XX:-CMSPrecleaningEnabled
不進行預清理。由於從新標記是獨佔 CPU 的,所以若是新生代 GC 發生以後,馬上出發一次新生代 GC,那麼停頓時間就會很長。爲了不這種狀況,預處理時會刻意等待一次新生代 GC 的發生,以後在進行預處理。
主要參數
啓動 CMS 回收器刻意使用參數:-XX:+UseConcMarkSweepGC
,線程併發數量刻意經過 -XX:ConcGCThreads
或 -XX:ParallelCMSThreads
參數設定。
此外,咱們還能夠設置 -XX:CMSInitiatingOccupancyFraction
來指定老年代空間使用閾值。當老年代空間使用率達到這個閾值時,會執行一次 CMS 回收,而不像其餘回收器同樣等到內存不夠用的時候才進行 GC。
咱們以前說過標記清除算法的缺點是會產生內存碎片,所以 CMS 回收器會產生較多內存碎片。咱們可使用 XX:+UseCMSCompactAtFullCollection
參數讓 CMS 在完成垃圾回收後,進行一次內存碎片整理。使用 -XX:CMSFullGCsBeforeCompaction
參數設置進行多少次 CMS 回收後,進行一次內存壓縮。
此外,若是但願使用 CMS 回收 Perm 區,那麼則能夠打開 -XX:+CMSClassUnloadingEnabled
開關。打開該開關後,若是條件容許,那麼系統會使用 CMS 的機制回收 Perm 區 Class 數據。
G1 回收器是 JDK 1.7 中使用的全新垃圾回收器,從長期目標來看,其是爲了取代 CMS 回收器。
G1 回收器擁有獨特的垃圾回收策略,和以前全部垃圾回收器採用的垃圾回收策略不一樣。從分代看,G1 依然屬於分代垃圾回收器。但它最大的改變是使用了分區算法,從而使得 Eden 區、From 區、Survivor 區和老年代等各塊內存沒必要連續。
在 G1 回收器以前,全部的垃圾回收器其內存分配都是連續的一塊內存,以下圖所示。
而在 G1 回收器中,其將一大塊的內存分爲許多細小的區塊,從而不要求內存是連續的。
從上圖能夠看到,每一個Region被標記了 E、S、O 和 H,說明每一個 Region 在運行時都充當了一種角色。全部標記爲 E 的都是 Eden 區的內存,它們散落在內存的各個角落,並不要求內存連續。同理,Survivor 區、老年代(Old)也是如此。
從上圖咱們還能夠看到 H 是以往算法中沒有的,它表明 Humongous。這表示這些 Region 存儲的是巨型對象(humongous object,H-obj),當新建對象大小超過 Region 大小一半時,直接在新的一個或多個連續 Region 中分配,並標記爲 H。
堆內存中一個 Region 的大小能夠經過 -XX:G1HeapRegionSize
參數指定,大小區間只能是1M、2M、4M、8M、16M 和 32M,總之是2的冪次方。若是G1HeapRegionSize 爲默認值,即把設置的最小堆內存按照2048份均分,最後獲得一個合理的大小。
工做步驟
G1 收集器的收集過程主要有四個階段:
新生代 GC 與其餘垃圾收集器的相似,就是清空 Eden 區,將存活對象移動到 Survivor 區,部分年齡到了就移動到老年代。
併發標記週期則分爲:初始標記、根區域掃描、併發標記、從新標記、獨佔清理、併發清理階段。其中初始標記、從新標記、獨佔清理是獨佔式的,會引發停頓。而且初始標記會引起一次新生代 GC。在這個階段,全部將要被回收的區域會被 G1 記錄在一個稱之爲 Collection Set 的集合中。
混合回收階段會首先針對 Collection Set 中的內存進行回收,由於這些垃圾比例較高。G1 回收器的名字 Garbage First 就是這個意思,垃圾優先處理的意思。在混合回收的時候,也會執行屢次新生代 GC 和 混合 GC,從而來進行內存的回收。
必要時進行 Full GC。當在回收階段遇到內存不足時,G1 會中止垃圾回收並進行一次 Full GC,從而騰出更多空間進行垃圾回收。
相關參數
打開 G1 收集器,咱們可使用參數:`-XX:+UseG1GC。
設置目標最大停頓時間,可使用參數:-XX:MaxGCPauseMillis
。
設置 GC 工做線程數量,可使用參數:-XX:ParallelGCThreads
。
設置堆使用率觸發併發標記週期的執行,可使用參數:-XX:InitiatingHeapOccupancyPercent
。
從一開始的串行回收器,到後來的並行回收器、CMS回收器,到最後的 G1 回收器,垃圾回收器不斷改進,使得垃圾回收效率不斷提高。特別是分區思想誕生後,對於垃圾回收停頓時間的控制更加細膩,可讓應用有更完美的延時控制,從而呈現更好的用戶體驗。
若是隻是看,其實沒法真正學會知識的。爲了幫助你們更好地學習,我建了一個虛擬機羣,專門討論學習 Java 虛擬機方面的內容,每週針對我所發文章進行討論答疑。若是你有興趣,關注「Java技術精選」公衆號,經過右下角菜單「入羣交流」加我好友,小助手會拉你入羣。