Java 主流垃圾收集器
文章目錄
- 垃圾收集器概述
- Serial 與 Serial Old 垃圾收集器
- Serial 與 Serial Old 垃圾收集器總結
- ParNew 垃圾收集器
- Parallel Scavenge 垃圾收集器
- Parallel Scavenge 的吞吐量控制參數
- Parallel Scavenge 的自適應調節策略
- Parallel Scavenge 垃圾收集器總結
- ParNew 和 Parallel Scavenge 垃圾收集器的對比
- Parallel Old 垃圾收集器
- CMS 垃圾收集器
- CMS 垃圾收集器的收集過程
- CMS 的缺點
- 處理器資源敏感
- Concurrent Mode Failure
- 浪費堆內存空間
- 沒有很好地處理內存碎片
- 無法處理浮動垃圾
- G1 垃圾收集器
- G1 垃圾收集器管理下的堆內存布局
- 區域的主要特性
- G1 垃圾收集的過程
- G1 垃圾收集動作分類
- Young GC/Minor GC
- Mixed GC
- Full GC
- G1 垃圾收集器總結
垃圾收集器概述
可以用一句非常形象的話來描述垃圾收集器:
如果說收集算法是內存回收的方法論,那么垃圾收集器就是內存回收的具體實現。這里我們主要介紹基于分代收集理論設計的垃圾收集器
Serial 與 Serial Old 垃圾收集器
Serial 與 Serial Old 垃圾收集器,都是單線程收集的垃圾收集器。
其中 Serial 用于收集新生代,使用的是 Appel 式復制算法;SerialOld 用于收集老年代,使用的是標記整理算法。
他們的特點也非常明顯,即都是單線程執行 GC 工作。
Serial 與 Serial Old 垃圾收集器總結
Serial 與 Serial Old 垃圾收集器優缺點如下:
優點:
- 實現簡單
- 單線程收集效率較高
- 資源消耗低
缺點:
- 無法運用多核 CPU 多線程并行處理優勢,導致整體工作效率低下
ParNew 垃圾收集器
ParNew 垃圾收集器,是一個支持多線程并行收集的新生代垃圾收集器,本質上是 Serial 的多線程并行版本。
ParNew 垃圾收集器,一個非常重要的特性就是除了 Serial 垃圾收集器外,只有它能跟 CMS 垃圾收集器搭配工作ParNew 使用的也是 Appel 式復制算法
Parallel Scavenge 垃圾收集器
Parallel Scanvenge 垃圾收集器,也是一個支持多線程并行收集的垃圾收集器。
Parallel Scanvenge 垃圾收集器關注的是吞吐量,所以也被稱為吞吐量優先收集器,吞吐量計算公式為:
吞吐量=運行用戶代碼時間運行用戶代碼時間+運行垃圾收集時間吞吐量=\frac{運行用戶代碼時間}{運行用戶代碼時間 + 運行垃圾收集時間} 吞吐量=運行用戶代碼時間+運行垃圾收集時間運行用戶代碼時間?
運行用戶代碼時間 + 運行垃圾收集時間基本上約等于程序運行總時間,則由公式可以得出:
- 運行垃圾收集時間與程序運行總時間的比值越小,則吞吐量越高
- 運行垃圾收集時間與程序運行總時間的比值越大,則吞吐量越低
Parallel Scavenge 的吞吐量控制參數
Parallel Scavenge 提供了兩個用于精準控制吞吐量的參數,分別是:
- 期望的最大垃圾收集停頓時間:-XX:MaxGCPauseMills,單位是毫秒
- 當設置了這個參數之后,收集器將盡力保證單次垃圾收集花費的時間不超過設定好的參數值
- 在需要與用戶頻繁交互的場景下,垃圾收集停頓時間越小,則用戶對于應用程序停頓的感知越少,即用戶體驗越好
- 此參數并不是越小越好,因為垃圾收集停頓時間的縮小是以犧牲吞吐量和新生代空間為代價來換取的
- 期望的最低吞吐量:-XX:GCTimeRatio
- 當設置了這個參數后,收集器將會盡量保證吞吐量大于等于設定好的參數
- 默認值是 99,即收集器將會盡量保證吞吐量大于等于 99%
Parallel Scavenge 的自適應調節策略
Parallel Scavenge 提供了一個自適應調節策略開關參數:-XX+UseAdaptiveSizePolicy。
當開啟這個參數后,我們就不需要再手工指定新生代大小,Eden 和 Survivor 大小比例等細節參數了。虛擬機將會根據當前應用的運行情況和系統資源,動態調整這些參數來使垃圾收集的性能最優,讓程序獲得最高的吞吐量。
注意,設置這個參數同時也可以設置期望的最大垃圾收集停頓時間、期望的最低吞吐量參數,來給虛擬機設定一個優化目標。
Parallel Scavenge 垃圾收集器總結
Parallel Scavenge 是一款支持多線程并行收集的垃圾收集器,可以通過參數來精準控制吞吐量;也可以開啟自適應調節策略,將虛擬機的一些運行細節參數交給虛擬機自行動態設置。
Parallel Scavenge 適用于計算密集型場景。
ParNew 和 Parallel Scavenge 垃圾收集器的對比
ParNew 和 Parallel Scavenge 都是支持多線程并行收集的垃圾收集器,它們之間的區別主要有以下幾點:
- Parallel Scavenge 可以精確控制吞吐量
- Parallel Scavenge 可以開啟自適應調節策略
- ParNew 可以與 CMS 搭配使用,Parallel Scavenge 不能
Parallel Old 垃圾收集器
Parallel Old 垃圾收集器,是一個支持多線程并行收集的老年代垃圾收集器。
Parallel Old 基于標記-整理算法實現,主要用于與 Parallel Scavenge 搭配使用。
JDK 8 默認的垃圾收集器組合就是 Parallel Scavenge 和 Parallel Old。
CMS 垃圾收集器
CMS 垃圾收集器,全稱 Concurrent Mark Sweep 垃圾收集器,是一種支持多線程并行清理,且能與用戶線程并發執行的垃圾收集器。從字面意思來看,可以理解為并發標記清除垃圾收集器,也被稱為并發低停頓收集器。
CMS 基于標記-清除算法實現。
CMS 垃圾收集器的收集過程
CMS 垃圾收集器的收集過程如下:
- 初始標記:執行 STW,并標記下 GC Root 能直接引用的對象。這個過程相當迅速
- 并發標記:退出 STW,多個 GC 線程從初始標記標記過的對象開始遍歷對象的引用關系圖
- 此過程多個 GC 線程與用戶線程一起運行,且耗時較長
- 重新標記:執行 STW,并修正并發標記期間標記產生變動的那一部分對象的標記記錄(采用增量更新的方式解決)
- 在并發標記過程中,GC 線程與用戶線程同時在運行,所以很可能會導致一些已經標記過的對象的存活狀態發生改變,故而需要 STW 來修正這些變動的標記
- 并發清理:退出 STW,GC 線程開始清理標記階段判定為死亡的對象
- 此過程多個 GC 線程與用戶線程一起運行
- 清理完成后不需要移動存活對象,即沒有整理內存碎片
- 并發重置:重置本次 GC 過程中的標記數據
- 此過程 GC 線程與用戶線程一起運行
整個過程如下所示:
CMS 的缺點
CMS 的優點非常明顯,即并發收集與低停頓。與此同時,也有幾個明顯的缺點
處理器資源敏感
CMS 垃圾收集器在與用戶線程并發執行的階段中,雖然不會暫停用戶線程,但是由于 GC 線程的執行本身就需要一部分系統資源,所以會搶占用戶線程的系統資源,導致用戶線程的執行變慢,從而降低總吞吐量。
這種情況在系統資源充足,處理器核心數比較多的場景中并不是很明顯(但多少還是會有影響),但是一旦處理器核心數較少時(少于 4 個),(GC 線程對資源的占用導致應用程序變慢的情況)可能就會變得比較明顯。
Concurrent Mode Failure
由收集過程我們可以知道,在并發標記和并發清理階段,用戶線程和 GC 線程是并發執行的。
那么在這兩個階段的實際的執行過程中,由于用戶線程在同時執行,即用戶線程在正常地調用、退出方法(創建和銷毀棧幀),聲明對象,或者修改引用關系,那么很可能出現再次觸發 GC 的情況(即用戶線程的這些操作又導致可用空閑不足或者觸發了其他垃圾回收的條件)。
這種情況就是 Concurrent Mode Failure,在這種情況發生后,會立即 STW,使用 Serial Old 垃圾收集器來回收。這不是個好消息,因為 Seial Old 的收集效率比較低下。
即當出現 Concurrent Mode Failure 時,JVM 會使用 Serial Old 垃圾收集器進行回收,本次的停頓時間將會變得很長。
浪費堆內存空間
由于在執行 GC 的過程中,還需要讓用戶線程繼續執行,所以觸發 GC 的條件不能像其他老年代垃圾收集器一樣,一直到快滿了才觸發,而是需要預留一部分空間,讓執行 GC 的過程中并發執行的用戶線程使用。
這個預留空間的大小(一個比例)通常可以配置,默認是 92。即觸發 GC 的條件是老年代的空間使用了 92% ,剩余的 8% 將預留給執行 GC 過程中并發執行的用戶線程使用。
這個空間并不是越小越好,當執行 GC 過程中,這個預留的空間不夠用而導致觸發一次新的 GC 時,將會出現一次 Concurrent Mode Failure。
所以,這里就需要有一個取舍:
- 如果這個預留空間設置得比較大,那么將會浪費內存空間
- 如果這個預留空間設置得比較小,那么可能會導致頻繁出現 Concurrent Mode Faliure,從而拉低吞吐量
沒有很好地處理內存碎片
CMS 是一款基于標記-清除算法實現的垃圾收集器,在擁有標記-清除算法帶來的優點的同時,不例外地,它也存在算法帶來的缺點——沒有整理內存碎片。
當內存碎片過多時,就會給大對象的內存分配帶來麻煩:老年代還剩余很多內存空間,但是卻找不到一塊足夠大的連續的內存空間,當出現這種情況時,虛擬機不得不發起一次 Full GC 來嘗試獲得一塊足夠大的連續的內存空間。
CMS 為解決這個問題,提供了兩個解決方案:
- 開啟 -XX:+UseCMSCompactAtFullCollection 參數,讓虛擬機在執行 Full GC 后執行一次碎片整理,開啟后基本等同于標記-整理算法
- 由于碎片整理需要移動存活的對象,所以這個過程是需要 STW 的,這會導致停頓時間變長,從而降低吞吐量
- 設置 -XX:CMSFullGCsBeforeCompaction 參數,即進行多少次 Full GC 后執行一次內存碎片整理
- 例如當此參數的值設置成 3 的含義代表著,進行了 3 次 Full GC 后在下次(即第 4 次) Full GC 執行前,先執行一次內存碎片整理后,再執行 Full GC
- 默認值為 0,即每次進行 Full GC 之前都要先整理一遍內存碎片
無法處理浮動垃圾
如果在兩個與用戶線程并發執行的過程中,又有新的垃圾對象產生,那么這部分對象將變成浮動垃圾,則本次清理過程并不能處理掉這些對象。
很多時候,浮動垃圾帶來了執行過程中的不確定性,例如很可能因為產生的浮動垃圾足夠多而重新觸發了一次新的 GC 過程,這就會引起 Concurrent Mode Failure,從而使吞吐量下降
G1 垃圾收集器
G1,即 Garbage First 垃圾收集器,它是一款面向服務端應用的垃圾收集器。在系統資源充足(大容量內存和 CPU 核心數較多)的情況下,可以同時滿足低停頓與高吞吐量的要求。
G1 垃圾收集器管理下的堆內存布局
G1 將堆劃分成了多個大小相等的獨立區域(Region),每個區域都可以根據當前內存分配需要而扮演不同的角色,例如扮演 Eden 空間,扮演 Survivor 空間,或者扮演老年代空間。
G1 可以根據每個區域當前扮演的角色不同,采用通過不同的策略進行管理。
區域的主要特性
- 區域最多為 2048 個
- 每個區域的大小取值范圍為 1~32MB,且必須為 2 的冪次方,可以通過參數 -XX:G1HeapRegionSize 來指定
- 區域有四種角色:
- Eden:表示這個區域當前是 Eden 空間的一部分
- Survivor:表示這個區域當前是 Survivor 空間的一部分
- Old:表示這個區域當前是老年代空間的一部分
- Humongous:表示當前這個區域存儲的是一個大對象(部分或全部,即一個大對象可能會存放在一個,或連續的幾個 Humongous 區域中)
- 當一個對象的大小超過了區域大小的 50%,那么就會被判定為大對象
- 新生代、老年代的概念依然存在,但是大小不再固定,空間也并不連續
- 新生代是由當前堆中所有扮演 Eden 和 Survivor 角色的區域的合集,這些區域并不一定連續,總大小也不固定
- 老年代是由當前堆中所有扮演老年代角色的區域的合集
G1 垃圾收集的過程
- 初始標記:進行 STW,并標記 GC Root 能直接引用的對象
- 并發標記:結束 STW,多個 GC 線程與用戶線程并發執行,遍歷上一步標記的對象的引用關系圖,判定對象的存活狀態
- 最終標記:進行 STW,使用原始快照(SATB)的方法處理上一步中對象引用關系存在變化的對象
- 篩選回收:進行 STW,對需要進行回收的 Region 進行回收價值和成本的排序,并結合用戶期望的停頓時間來制定回收計劃
- 最終制定的回收計劃中,需要回收的 Region 當前所扮演的角色可能是不同的,但是一定是當前最值得回收的一些區域
- 回收時,將會把這些 Region 中存活的對象全部復制到空的 Region 中,再清空舊 Region 的空間
- 用戶期望的停頓時間對于決定最后要回收的回收集是相當重要的,如果這個值越小,那么可能最后要回收的回收集的數量也越少,檔次回收效率也會變低
G1 垃圾收集動作分類
Young GC/Minor GC
Young GC(或者說 Minor GC),回收范圍為年輕代。當現有的所有的 Eden 區放滿了不會立馬觸發,而是會估算現有的 Eden 區回收需要的時間,估算出來的值與用戶指定的最大停頓時間進行對比:
- 如果估算出來的回收現有 Eden 區所需的時間遠小于用戶指定的最大停頓時間,那么將不會執行 Young GC,而是會增加年輕代的 Region
- 如果估算出來的回收現有 Eden 區所需的時間接近于用戶指定的最大停頓時間,那么將執行 Young GC
Mixed GC
Mixed GC,回收范圍為所有區域,當老年代的區域占有率達到設定的參數值時觸發。
使用復制算法,當執行拷貝發現已經沒有足夠可用的空 Region 區域時,將會觸發一次 Full GC
Full GC
Full GC,回收范圍為所有區域。當 Mixed GC 執行中發現沒有足夠可用的空 Region 區域時觸發。
Full GC 將會進行 STW,然后采用單線程進行標記、清除、壓縮整理,以便釋放出新的空 Region。
G1 垃圾收集器總結
G1 垃圾收集器的主要特點如下:
- 獨特的堆內存劃分方式
- 良好的內存碎片整理機制:G1 整體是基于標記-整理算法來實現的,但是從局部來看是基于復制算法來看實現的
- 可預測的停頓時間模型:用戶通過指定 -XX:MaxGCPauseMills 來確切地指定每次 GC 的最大停頓時間
總結
以上是生活随笔為你收集整理的Java 主流垃圾收集器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎么让图片手机上排列_PS手机卷轴样机,
- 下一篇: python 样本均值t检验_假设检验与