在上一篇文章中,我們介紹了Java的垃圾回收機制,包括什麼時候回收垃圾,标記垃圾的算法以及回收垃圾的算法。這篇文章我們主要來介紹Java的垃圾收集器。
在介紹垃圾收集器之前,我們首先需要知道一些必要的概念。
Stop the world
顧名思義,“Stop the world”就是 JVM 由于要執行 GC 而停止了其他應用程式的運作,在任何 GC 算法中都可能會發生。假設有這麼一個場景,你的程式正在愉快的運作,突然之間 JVM 要清理垃圾了。然後程式就陷入了10分鐘的等待,是不是很抓狂?當然一般情況下會讓你等待這麼久,但是“Stop the world”會在一定程度上影響使用者體驗這是毋庸置疑的。是以,多數GC優化通過減少 Stop-the-world 時間來提升系統性能。
垃圾收集器和垃圾回收算法的關系
說完“Stop the world”我們回到正題,回到我們垃圾收集器的子產品,如果說垃圾收集算法是記憶體回收的方法論,那麼垃圾收集器那麼垃圾收集器就是記憶體回收的具體實作。
我們費盡心機對垃圾收集器進行比較,目的不是挑出一個最好的垃圾收集器,而是找到最合适的。 現在為止還沒有最好的垃圾收集器出現,更沒有說出現萬能的垃圾收集器,我們能做的就是根據具體應用場景選擇适合自己的垃圾收集器。試想一下:如果 “完美垃圾收集器” 真的面世了,我們還需要做這些工作嗎?
常用的垃圾收集器
Serial 收集器
Serial收集器是最基本、曆史最悠久的垃圾收集器。它是一個單線程收集器,“單線程” 的意義不僅僅意味着它隻會使用一條垃圾收集線程去完成垃圾收集工作,更重要的是 它在進行垃圾收集工作的時候必須暫停其他所有的工作線程( "Stop The World" ),直到它收集結束。 它會在使用者不可見的情況下把使用者正常工作的線程全部停掉。想象一下,當你結束一天的工作回到家中,喝着冰闊樂刷着副本正要打Boss,突然你的電腦說他要休息5分鐘,你會是什麼感覺?
存在即合理,當然Serial 收集器也有優于其他垃圾收集器的地方,它簡單而高效(與其他收集器的單線程相比)。Serial 收集器由于沒有線程互動的開銷,自然可以獲得很高的單線程收集效率。Serial 收集器對于運作在 Client 模式下的虛拟機來說是個不錯的選擇。
它的 新生代采用複制算法,老年代采用标記整理算法。
ParNew 收集器
ParNew 收集器是 Serial 收集器的多線程版本,除了使用多線程進行垃圾收集之外,其餘行為(控制參數、收集算法、配置設定規則、回收政策等等)和 Serial 收集器完全一樣。
除了支援多線程收集,ParNew 相對 Serial 似乎并沒有太多改進的地方。但是它卻是許多運作在 Server 模式下的虛拟機的首要選擇,除了 Serial 收集器外,隻有它能與 CMS 收集器(真正意義上的并發收集器,後面會介紹到)配合工作。ParNew單核狀态下不如Serial,多核線程下才有優勢。
新生代采用複制算法,老年代采用标記整理算法。
Parallel Scavenge 收集器
Parallel Scavenge 收集器是一個新生代收集器,也是采用複制算法+并行。聽起來和ParNew差不多對不對,那麼它有什麼特别之處呢?
Parallel Scavenge 收集器關注點是吞吐量(CPU運作代碼的時間與CPU總消耗時間的比值)。 而CMS 等垃圾收集器的關注點更多的是縮短使用者線程的停頓時間(提高使用者體驗)。停頓時間越短就越适合和使用者進行互動(響應速度快,可以優化使用者體驗),而高吞吐量則可以高效的利用CPU時間,盡快完成使用者的計算任務。
Parallel Scavenge 收集器提供了很多參數供使用者找到最合适的停頓時間或最大吞吐量,如果對于收集器運作不太了解的話,手工優化存在的話可以選擇把記憶體管理優化交給虛拟機去完成也是一個不錯的選擇。
Serial Old 收集器
Serial 收集器的老年代版本,它同樣是一個單線程收集器。它主要有兩大用途:一種用途是在 JDK1.5 以及以前的版本中與 Parallel Scavenge 收集器搭配使用,另一種用途是作為 CMS 收集器的後備方案。
新生代采用複制算法,老年代采用标記整理算法。
Parallel Old 收集器
Parallel Scavenge 收集器的老年代版本。使用多線程和“标記-整理”算法。在注重吞吐量以及 CPU 資源的場合,都可以優先考慮 Parallel Scavenge 收集器和 Parallel Old 收集器。
CMS 收集器
CMS(Concurrent Mark Sweep)收集器是一種以擷取最短回收停頓時間為目标的收集器。它非常重視服務的響應速度,以期給使用者最好的體驗。。
從名字中的Mark Sweep這兩個詞可以看出,CMS 收集器是一種 “标記-清除”算法實作的,它的運作過程相比于前面幾種垃圾收集器來說更加複雜一些。整個過程分為四個步驟:
- 初始标記 需要“Stop the world”,僅僅隻是标記一下GC Roots 能直接關聯到的對象,速度很快。
- 并發标記 并發追溯标記,程式不會停頓。
- 重新标記 需要“Stop the world”修正并發标記期間因使用者程式繼續運作而導緻标記産生變動的那一部分對象的标記記錄
- 并發清除 清理垃圾對象,程式不會停頓
CMS一款優秀的垃圾收集器,主要優點:并發收集、低停頓。但是它有下面三個明顯的缺點:
- 對 CPU 資源敏感;
- 無法處理浮動垃圾;
- 它使用的回收算法-“标記-清除”算法會導緻收集結束時會有大量空間 碎片産生。
G1 收集器
G1 (Garbage-First) 是一款面向伺服器的垃圾收集器,開發人員希望在未來可以換掉CMS收集器,它有如下特點
- 并行與并發:G1 能充分利用 CPU、多核環境下的硬體優勢,使用多個 CPU(CPU 或者 CPU 核心)來縮短 Stop-The-World 停頓時間。部分其他收集器原本需要停頓 Java 線程執行的 GC 動作,G1 收集器仍然可以通過并發的方式讓 java 程式繼續執行。
- 分代收集:雖然 G1 可以不需要其他收集器配合就能獨立管理整個 GC 堆,但是還是保留了分代的概念。
- 空間整合:與 CMS 的“标記--清理”算法不同,G1 從整體來看是基于“标記整理”算法實作的收集器;從局部上來看是基于“複制”算法實作的。這就意味着不會産生大量的記憶體碎片
- 可預測的停頓:這是 G1 相對于 CMS 的另一個大優勢,降低停頓時間是 G1 和 CMS 共同的關注點,但 G1 除了追求低停頓外,還能建立可預測的停頓時間模型,能讓使用者明确指定在一個長度為 M 毫秒的時間片段内。
G1 收集器的運作大緻分為以下幾個步驟:
- 初始标記
- 并發标記
- 最終标記
- 篩選回收
G1收集器将整個Java堆記憶體劃分為若幹個記憶體大小相等的Region,年輕代和老年代不再實體隔離,他們都是一部分Region的集合。
posted on 2019-06-17 21:26 将圖南 閱讀(...) 評論(...) 編輯 收藏