說明:兩個收集器之間存在連線說明它們可以搭配使用.虛擬機所處的區域則表明它屬於新生代收集器還是老年代收集器.
Serial收集器
這個收集器是一個單線程收集器,但是它是"單線程"的意義,並不僅僅說明它只會使用一個CPU或一條收集器去完成垃圾收集工作,更重要的是它進行垃圾收集時,必須暫停其他所有的工作線程,直到它收集結束.
優點:簡單而高效(與其他收集器的單線程相比),對於限定單個CPU的環境來說,Serial收集器由於沒有線程交互的開銷,專心做垃圾收集自然可以獲得最高的單線程收集效率.
ParNew收集器
ParNew收集器起始就是Serial收集器的多線程版本.
除了Serial收集器外,目前只有ParNew能與CMS收集器配合工作.
Parallel Scavenge收集器
Parallel Scavenge收集器是一個新生代收集器,也是使用復制算法的收集器,又是並行的多線程收集器.
Parallel Scavenge收集器的目標是達到一個可控制的吞吐量(Throughout).
吞吐量就是CPU用於運行用戶代碼的時間與CPU消耗時間的比值.
吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間)
停頓時間越短就越適合需要與用戶交互的程序,良好的相應速度能提升用戶體驗,而吞吐量則可以高效率地利用CPU時間,盡快完成程序的運算任務,主要適合在後台運算而不需要太多交互的任務.
Parallel Scavenge收集器提供了兩個參數用於精確控制吞吐量,分別是控制最大對大垃圾收集停頓時間的-xx:MaxGCPauseMillis參數以及直接設置吞吐量大小的-xx:GCTimeRauo參數.
Serial Old收集器
Serial Old 是Serial收集器的老年代版本,是一個單線程的收集器.使用"標記-整理"算法.
兩大用途:
a)在JDK1.5以及之前的版本中與Parallel Scavenge收集器搭配使用.
b) 作為CMS收集器的後備預案,在並發收集發生Concurrent Mode Failure 時使用.
Parallel Old收集器
Parallel Old是Parallel Scavenge收集器的老年代版本,使用多線程和"標記-整理"算法.
在注重吞吐量以及CPU資源敏感的場合,都可以優先考慮Parallel Scavenge加Parallel Old收集器.
CMS收集器
CMS(Concurrent Mark Sweep)收集器是一種一獲取最短回收停頓時間為目標的收集器.
CMS收集器是基於"標記-清除"算法實現的,整個過程分為4個步驟(仍然需要"Stop The Word"):
I.初始化標記(CMS Concurrent Mark)
II.並發標記(CMS Concurrent Mark)
III.重新標記(CMS Remark)
IV.並發請求(CMS Concurrent Sweeep)
初始化標記僅僅是標記一下GC Root能直接關聯到的對象,速度很快,並發標記階段是進行GC Root Tracing的過程,而重新標記階段則是為了修正並發標記期間因用戶程序繼續運行而導致標記產生變動的那一部分對象的標記記錄,這個階段的停頓時間一般會比初始標記階段稍長一些,但這遠比並發標記的時間短.
從總體上說,CMS收集器的內存回收過程是與用戶線程一起並發執行的.
缺點:
I.CMS收集器對CPU資源非常敏感
II.CMS收集器無法處理浮動垃圾(Floating Garbage),可能出現"Concurrent Mode Failure"失敗而導致另一次Full GC的產生.
III.CMS是基於"標記-清除"算法實現的收集器,意味著收集結束時會有大量的空間碎片產生.
G1收集器
G1(Garbage-First)收集器是當今收集器是當今收集器技術發展的最前沿成果之一.
G1是一款面向服務端應用的垃圾收集器,具備一下節點:
I.並行與並發:G1能充分利用多CPU,多核環境下的硬件優勢,使用多個CPU來縮短Stop_The_Word停頓的時間,部分其他收集器原本需要停頓Java線程執行的GC動作,G1收集器仍然可以通過並發的方式讓Java程序繼續執行.
II.分代收集
III.空間整合:G1從整體來看是基於"標記-整理"算法實現的收集器.從局部(兩個Regin之間)上看是基於"復制"算法實現的,但是無論如何,這兩種算法都意味著G1運行期間不會產生內存空間碎片,收集後能提供規整的可用內存.這種特性有利於程序長時間運行,分配大對象時不會因為無法找到連續空間而提前觸發下一次GC.
IV.可預測的停頓:這是G1相對於CMS的另一大優勢,降低停頓時間是G1和CMS共同的關注點,但是G1除了追求低停頓外,還能建立可預測的時間模型,能讓使用者明確指定在一個長度為M毫秒的時間片段內,消耗在垃圾收集上的時間不得超過N毫秒,這幾乎已經是實時Java的垃圾收集器的特征了.
使用G1收集器時,Java堆的內存布局就與其他收集器有很大差別,他將整個Java堆劃分為多個大小相等的獨立區域(Regin),雖然還保留有新生代和老年代的概念,但新生代和老年代不再是物理隔離的了,它們都是一部分Regin(不需要連續)的集合.
G1收集器之所以能建立可預測的停頓時間模型,是因為它可以有計劃地避免在整個Java堆中進行全區域的垃圾收集.G1跟蹤各個Regin裡面的垃圾堆積的價值大小,在後台維護一個有限列表,每次根據允許的收集時間,優化回收價值最大的Regin(這就是Garbage-First名稱的由來).
在G1收集器中.Regin之間的對象引用以及其他收集器中的新生代與老年代之間的對象引用,虛擬機都是使用Remenbered Set 來避免全堆掃描的.G1中每個Regin都有一個與之對應的Remenbered Set,虛擬機發現程序在對Reference類型的數據進行寫操作時,會產生一個Write Barrier暫時中斷寫操作,檢查Reference引用的對象是否處於不同的Regin之中(在分代的例子中就是檢查是否老年代中的對象引用了新生代的對象).如果是,便通過CardTable把相關引用信息記錄到被引用對象所屬的Regin的Remenbered Set即可保證不對全堆掃描也不會有遺漏.
G1收集器的運作大致可劃分為一下幾個步驟:
I.初始化標記(Initial Marking)
II.並發標記(Concurrent Marking)
III.最終標記(Final Marking)
IV. 篩選回收(Live Data Counting and Evacuation)
初始標記階段僅僅是標記一下GC Roots能直接關聯到的對象,並且修改TAMS(Next Top at Mark Start)的值,讓下一階段用戶程序並發運行時,能在正確可用的Regin中創建對象.這階段需要停頓線程,但耗時很短.
並發標記階段是從GC Root 開始對堆中對象進行可達性分析,找出存活的對象,這階段耗時較長,但可與用戶進程並發執行.
最終標記階段則是為了修正在並發標記期間因用戶程序繼續執行運作導致標記產生變動的那一部分標記記錄,虛擬機將這段時間對象變化記錄在線程Remenbered Set Logs裡面,最終標記階段需要把Remenbered Set Logs的數據合並到Remenbered Set中,這個階段需要停頓線程,但是可並行執行.
篩選回收階段首先對各個Regin的回收價值和成本進行排序,根據用戶所期望的GC停頓時間來制定回收計劃.這個階段也可以做到與用戶程序一起並發執行,但是因為回收一部分Regin,時間是用戶可控制的,而且停頓用戶線程將大幅度提高收集效率.