重識JVM(五):垃圾收集器

如果說收集算法是內存回收的方法論,那麼垃圾收集器就是內存回收的具體實現了。

基於HotSpot 虛擬機的所有收集器如圖所示:

上圖中展示了7種用於不同分代的收集器,兩個收集器之間存在連線的,就說明它們可以搭配使用。下面讓我們來一個一個瞭解一下它們吧。

Serial 收集器

Serial 是最基本、歷史最悠久的垃圾收集器,使用複製算法。Serial 是一個單線程的收集器,它不僅僅只會使用一個CPU 或一條線程去完成垃圾收集工作,並且在進行垃圾收集的同時,必須暫停其他所有的工作線程,直到垃圾收集結束。Serial 垃圾收集器雖然在收集垃圾過程中需要暫停所有其他的工作線程,但是它簡單高效,對於限定單個CPU 環境來說,沒有線程交互的開銷,可以獲得最高的單線程垃圾收集效率,因此Serial 垃圾收集器依然是java 虛擬機運行在Client 模式下默認的新生代垃圾收集器。

ParNew 收集器

ParNew 垃圾收集器其實是Serial 收集器的多線程版本,也使用複製算法,除了使用多線程進行垃圾收集之外,其餘的行爲和Serial 收集器完全一樣,ParNew 垃圾收集器在垃圾收集過程中同樣也要暫停所有其他的工作線程。ParNew 雖然是除了多線程外和Serial 收集器幾乎完全一樣,但是ParNew 垃圾收集器是很多java 虛擬機運行在Server 模式下新生代的默認垃圾收集器。

Parallel Scavenge 收集器

Parallel Scavenge 收集器也是一個新生代垃圾收集器,同樣使用複製算法,也是一個多線程的垃圾收集器,它重點關注的是程序達到一個可控制的吞吐量(Thoughput,CPU 用於運行用戶代碼的時間/CPU 總消耗時間,即吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間)),高吞吐量可以最高效率地利用CPU 時間,儘快地完成程序的運算任務,主要適用於在後臺運算而不需要太多交互的任務。

Serial Old 收集器

Serial Old 是Serial 垃圾收集器年老代版本,它同樣是個單線程的收集器,使用標記-整理算法,這個收集器也主要是運行在Client 默認的java 虛擬機默認的年老代垃圾收集器。
Server 模式下,主要有兩個用途:
1.在JDK1.5 之前版本中與新生代的Parallel Scavenge 收集器搭配使用。
2.作爲年老代中使用CMS 收集器的後備垃圾收集方案。

Parallel Old 收集器

Parallel Old 收集器是Parallel Scavenge 的年老代版本,使用多線程的標記-整理算法,在JDK1.6 纔開始提供。在JDK1.6 之前,新生代使用ParallelScavenge 收集器只能搭配年老代的Serial Old 收集器,只能保證新生代的吞吐量優先,無法保證整體的吞吐量,Parallel Old 正是爲了在年老代同樣提供吞吐量優先的垃圾收集器,如果系統對吞吐量要求比較高對CPU比較敏感,可以優先考慮新生代Parallel Scavenge 和年老代Parallel Old 收集器的搭配策略

CMS 收集器(重點)

Concurrent mark sweep(CMS)收集器是一種年老代垃圾收集器,其最主要目標是獲取最短垃圾回收停頓時間,和其他年老代使用標記-整理算法不同,它使用多線程的標記-清除算法。最短的垃圾收集停頓時間可以爲交互比較高的程序提高用戶體驗,CMS 收集器是SunHotSpot 虛擬機中第一款真正意義上併發垃圾收集器,它第一次實現了讓垃圾收集線程和用戶線程同時工作。
CMS 工作機制相比其他的垃圾收集器來說更復雜,整個過程分爲以下4 個階段:
1.初始標記:只是標記一下GC Roots 能直接關聯的對象,速度很快,仍然需要暫停所有的工作線程
2.併發標記:進行GC Roots 跟蹤的過程,和用戶線程一起工作,不需要暫停工作線程。
3.重新標記:爲了修正在併發標記期間,因用戶程序繼續運行而導致標記產生變動的那一部對象的標記記錄,仍然需要暫停所有的工作線程
4.併發清除:清除GC Roots 不可達對象,和用戶線程一起工作,不需要暫停工作線程。
由於耗時最長的併發標記和併發清除過程中,垃圾收集線程可以和用戶現在一起併發工作,所以總體上來看CMS 收集器的內存回收和用戶線程是一起併發地執行。

下圖是CMS 收集器運行示意圖:

CMS 收集器有以下三個不足
1.CMS 收集器對CPU 資源非常敏感,其默認啓動的收集線程數=(CPU 數量+3)/4,在用戶程序本來CPU 負荷已經比較高的情況下,如果還要分出CPU 資源用來運行垃圾收集器線程,會使得CPU 負載加重。
2.CMS 無法處理浮動垃圾(Floating Garbage),可能會導致Concurrent ModeFailure 失敗而導致另一次Full GC由於CMS 收集器和用戶線程併發運行,因此在收集過程中不斷有新的垃圾產生,這些垃圾出現在標記過程之後,CMS 無法在本次收集中處理掉它們,只好等待下一次GC 時再將其清理掉,這些垃圾就稱爲浮動垃圾。CMS 垃圾收集器不能像其他垃圾收集器那樣等待年老代機會完全被填滿之後再進行收集,需要預留一部分空間供併發收集時的使用,可以通過參-XX:CMSInitiatingOccupancyFraction 來設置年老代空間達到多少的百分比時觸發CMS 進行垃圾收集,默認是68%。如果在CMS 運行期間,預留的內存無法滿足程序需要,就會出現一次ConcurrentModeFailure 失敗此時虛擬機將啓動預備方案,使用Serial Old 收集器重新進行年老代垃圾回
收。

3.CMS 收集器是基於標記-清除算法,因此不可避免會產生大量不連續的內存碎片,如果無法找到一塊足夠大的連續內存存放對象時,將會觸發因此Full GC。

G1 收集器(重點)

相比與CMS 收集器,G1 收集器兩個最突出的改進是:
1.基於標記-整理算法,不產生內存碎片
2.可以預測停頓時間,在不犧牲吞吐量前提下,實現低停頓垃圾回收。
G1 收集器避免在整個java堆中進行全區域垃圾收集,它把堆內存劃分爲大小固定的幾個獨立區域,並且跟蹤這些區域的垃圾收集的價值大小(回收所獲得空間大小以及回收所需要的時間的經驗值),在後臺維護一個優先級列表,每次根據所允許的收集時間,回收價值最大的區域。區域劃分和優先級區域回收機制,確保G1 收集器可以在有限時間獲得最高的垃圾收集效率。

G1收集器的運作大致可劃分爲以下幾個步驟:

1.初始標記:只是標記一下GC Roots 能直接關聯的對象,速度很快,仍然需要暫停所有的工作線程
2.併發標記:進行GC Roots 跟蹤的過程,和用戶線程一起工作,不需要暫停工作線程。
3.最終標記:爲了修正在併發標記期間,因用戶程序繼續運行而導致標記產生變動的那一部對象的標記記錄,仍然需要暫停所有的工作線程
4.篩選回收:首先對各個區域的回收價值和成本進行排序,根據用戶所期望的GC停頓時間來指定回收計劃。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章