在涉及 Java 相關的面試中,面試官常常會讓講講 Java 中的垃圾收集相關的理解和常見的分類。可見,光就應付面試而言,JVM 的垃圾收集也對每一位 Java 開發者很重要。除此以外,對於咱們瞭解和解決 Java 應用的性能時,也頗有幫助。面試
在上一篇介紹了 Java 虛擬機內存的垃圾收集算法。本章將會介紹 Java 中經常使用的垃圾收集器及其特性。算法
本文的主要內容:微信
在介紹具體的垃圾回收器以前,咱們先了解幾個基本概念。多線程
計算機系統的信息交換有兩種方式:並行數據傳輸方式和串行數據傳輸方式。閉包
舉個簡單的例子,寫代碼和聽音樂這兩件事:併發
讀者能夠思考上面所述的串行、並行和併發。jvm
在 JVM 垃圾收集器中也涉及到如上的三個概念。分佈式
在瞭解了這些概念以後,咱們開始具體介紹經常使用的垃圾收集器。性能
如上所述,串行回收器是指使用單線程進行垃圾回收的回收器,每次回收時串行回收器只有一個工做線程,對於併發能力較弱的計算機來講,串行回收器的專一性和獨佔性每每有更好的表現。串行回收器能夠在新生代和老年代使用,根據做用的堆空間不一樣,分爲新生代串行回收器和老年代串行回收器。優化
Serial收集器是最古老的收集器,它的缺點是當Serial收集器想進行垃圾回收的時候,必須暫停用戶的全部進程,即 STW(服務暫停)。到如今爲止,它依然是虛擬機運行在 client 模式下的默認新生代收集器。
參數控制:-XX:+UseSerialGC
使用串行收集器。
Serial 收集器的老年代版本,它一樣是一個單線程收集器。它主要有兩大用途:一種用途是在 JDK1.5 以及之前的版本中與 Parallel Scavenge 收集器搭配使用,另外一種用途是做爲 CMS 收集器的後備方案。
UseSerialGC:開啓此參數使用 Serial & Serial Old 蒐集器(client 模式默認值)。
並行回收器是在串行回收器的基礎上作了改進,它可使用多個線程同時進行垃圾回收,對於計算能力強的計算機來講,能夠有效的縮短垃圾回收所需的實際時間。
ParNew 收集器是一個工做在新生代的垃圾收集器,它只是簡單的將串行收集器多線程化,它的回收策略和算法和串行回收器同樣。新生代並行,老年代串行;新生代複製算法、老年代標記-整理。
參數控制:-XX:+UseParNewGC
使用 ParNew 收集器;-XX:ParallelGCThreads
限制線程數量 除了 Serial 收集器外,只有它能與 CMS 收集器(真正意義上的併發收集器,後面會介紹到)配合工做。
Parallel 是採用複製算法的多線程新生代垃圾回收器,Parallel 收集器更關注系統的吞吐量。所謂吞吐量就是 CPU 用於運行用戶代碼的時間與CPU總消耗時間的比值,即吞吐量 = 運行用戶代碼時間/(運行用戶代碼時間 + 垃圾收集時間)。
停頓時間越短就越適合須要與用戶交互的程序,良好的響應速度可以提高用戶的體驗;
而高吞吐量則能夠最高效率地利用CPU時間,儘快地完成程序的運算任務,主要適合在後臺運算而不須要太多交互的任務。
能夠經過參數來打開自適應調節策略,虛擬機會根據當前系統的運行狀況收集性能監控信息,動態調整這些參數以提供最合適的停頓時間或最大的吞吐量;也能夠經過參數控制GC的時間不大於多少毫秒或者比例;新生代複製算法、老年代標記-整理
參數控制:
Parallel Old 收集器是 Parallel Scavenge 收集器的老年代版本,採用多線程和標記-整理
算法,也是比較關注吞吐量。在注重吞吐量及 CPU 資源敏感的場合,均可以優先考慮 Parallel 加 Parallel Old 收集器。
參數控制:-XX:+UseParallelOldGC
使用 Parallel Old 收集器;-XX:ParallelGCThreads
限制線程數量。
CMS(Concurrent Mark Sweep) 併發標記請除,它使用的是標記-清除法,工做在老年代,主要關注系統的停頓時間。
CMS 並非獨佔的回收器,也就是說,CMS 回收的過程當中應用程序仍然在不停的工做,又會有新的垃圾不斷的產生,因此在使用CMS的過程當中應該確保應用程序的內存足夠可用,CMS不會等到應用程序飽和的時候纔去回收垃圾,而是在某一閥值(默認爲68)的時候開始回收,也就是說當老年代的空間使用率達到68%的時候會執行CMS。若是內存使用率增加很快,在CMS執行過程當中,已經出現了內存不足的狀況,此時,CMS回收就會失敗,虛擬機將啓動老年代 Serial 進行垃圾回收,這會致使應用程序中斷,直到垃圾回收完成後纔會正常工做,這個過程GC的停頓時間可能較長,因此閥值的設置要根據實際狀況設置。
主要優勢:併發收集、低停頓。可是它有下面三個明顯的缺點:
標記-清除
算法會致使收集結束時會有大量空間碎片產生。標記清除法產生的內存碎片問題,CMS 提供提供了一些優化設置,能夠設置完成 CMS 以後進行一次碎片整理,也能夠設置進行多少次 CMS 回收後進行碎片整理。
參數控制:
-XX:+UserConcMarkSweepGC
使用 CMS 垃圾清理器-XX:CMSInitatingPermOccupancyFraction
設置閥值-XX:ConcGCThreads
限制線程數量-XX:+UseCMSCompactAtFullCollection
設置完成 CMS 以後進行一次碎片整理-XX:CMSFullGCsBeforeCompaction
設置進行多少次 CMS 回收後進行碎片整理G1(Garbage First) 垃圾收集器是當今垃圾回收技術最前沿的成果之一。早在 JDK7 就已加入 JVM 的收集器你們庭中,成爲 HotSpot 重點發展的垃圾回收技術。
G1 收集器在後臺維護了一個優先列表,每次根據容許的收集時間,優先選擇回收價值最大的 Region。包括:Eden、Survivor、Old 和 Humongous。
其中,Humongous 是特殊的 Old 類型,回收空閒巨型分區,專門放置大型對象。這樣的劃分方式意味着不須要一個連續的內存空間管理對象。G1 將空間分爲多個區域,優先回收垃圾最多的區域。一個對象和它內部所引用的對象可能不在同一個 Region 中,那麼當垃圾回收時,是否須要掃描整個堆內存才能完整地進行一次可達性分析?
固然不是,每一個 Region 都有一個 Remembered Set(已記憶集合),用於記錄本區域中全部對象引用的對象所在的區域,從而在進行可達性分析時,只要在 GC Roots 中再加上 Remembered Set 便可防止對全部堆內存的遍歷。
同 CMS 垃圾回收器同樣,G1 也是關注最小時延的垃圾回收器,也一樣適合大尺寸堆內存的垃圾收集,官方也推薦使用 G1 來代替選擇 CMS。G1 最大的特色是引入分區的思路,弱化了分代的概念,合理利用垃圾收集各個週期的資源,解決了其餘收集器甚至 CMS 的衆多缺陷。
G1收集器的運做大體分爲如下幾個步驟:
G1 能充分利用多 CPU、多核環境下的硬件優點,使用多 CPU(CPU 或者 CPU 核心)來縮短 Stop-The-World 停頓的時間,部分其餘收集器本來須要停頓 Java 線程執行的GC動做,G1 收集器仍然能夠經過併發的方式讓 Java 程序繼續執行。
此外,與其餘收集器同樣,分代概念在G1中依然得以保留。雖然 G1 能夠不需其餘收集器配合就能獨立管理整個 GC 堆,但它可以採用不一樣的方式去處理新建立的對象和已經存活了一段時間、熬過屢次 GC 的舊對象以獲取更好的收集效果。
空間整合:與 CMS 的 標記-清理
算法不一樣,G1 從總體看來是基於 標記-整理
算法實現的收集器,從局部(兩個 Region 之間)上看是基於 複製
算法實現,不管如何,這兩種算法都意味着 G1 運做期間不會產生內存空間碎片,收集後能提供規整的可用內存。這種特性有利於程序長時間運行,分配大對象時不會由於沒法找到連續內存空間而提早觸發下一次 GC。
可預測的停頓:這是 G1 相對於 CMS 的另一大優點,下降停頓時間是 G1 和 CMS 共同的關注點,但 G1 除了追求低停頓外,還能創建可預測的停頓時間模型,能讓使用者明確指定在一個長度爲M毫秒的時間片斷內,消耗在垃圾收集上的時間不得超過 N 毫秒,這幾乎已是實時 Java(RTSJ)的垃圾收集器特徵了。
參數控制:-XX:+UseG1GC
。
本文介紹了常見的7種不一樣分代的收集器:Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS、G1;而它們所處區域,則代表其是屬於新生代收集器仍是老年代收集器:
根據收集的區域(年輕代或年老代)和收集器自身的特性,能夠有以下組合: Serial/Serial Old、Serial/CMS、ParNew/Serial Old、ParNew/CMS、Parallel/Serial Old、Parallel/Parallel Old、G1。
ZGC 來了。ZGC 是 JDK11 中要發佈的最新垃圾收集器。徹底沒有分代的概念,官方給出 ZGC 的優勢是無碎片,時間可控,超大堆。讀者能夠嘗試瞭解和使用一下 ZGC 。