1 垃圾回收相關算法
垃圾回收器首先要做的就是,判斷一個對象是存活狀態還是死亡狀態,死亡的對象將會被標識為垃圾數據並等待收集器進行清除。
- 判斷一個對象是否為死亡狀態的常用算法有兩個:引用計數器算法 、可達性分析算法。
- 垃圾回收的常見算法有以下幾個:標記-清除算法、標記-復制算法、標記-整理算法。
1.1 引用計數算法(Reference Counting)
在創建對象時關聯一個與之相對應的計數器,當此對象被使用時加 1,相反銷毀時 -1。當此計數器為 0 時,則表示此對象未使用,可以被垃圾收集器回收。
引用計數算法的優缺點很明顯,其優點是垃圾回收比較及時,實時性比較高,只要對象計數器為 0,則可以直接進行回收操作;而缺點是無法解決循環引用的問題。
循環引用示例:
public class RCTest {
static class Test {
public Test object = null;
}
public static void main(String[] args) {
Test object1 = new Test();
Test object2 = new Test();
object1.object = object2;
object2.object = object1;
object1 = null;
object2 = null;
}
}
1.2 可達性分析算法(Reachability Analysis)
指從對象的起點(GC Roots)開始向下搜索,如果對象到 GC Roots 沒有任何引用鏈相連時,也就是說此對象到 GC Roots 不可達時,則表示此對象可以被垃圾回收器所回收。
在Java語言中,可作為GC Roots的對象包括下面幾種:
- 棧幀中的局部變量表中的 reference 引用所引用的對象
- 方法區中 static 靜態引用的對象
- 方法區中 final 常量引用的對象
- 本地方法棧中 JNI(即Native方法) 引用的對象
- Java虛擬機內部的引用,如基本數據類型對應的 Class 對象,一些常駐的異常對象(比如 NullPointExcepiton、OutOfMemoryError) 等,還有系統類加載器
- 所有被同步鎖持有的對象,比如被 synchronized 持有的對象
- 反映Java虛擬機內部情況的 JMXBean、 JVMTI 中注冊的回調、 本地代碼緩存等
finalize()
當使用可達性分析判斷一個對象不可達時,並不會直接標識這個對象為死亡狀態,而是先將它標記為“待死亡”狀態再進行一次校驗。
校驗的內容就是此對象是否重寫了 finalize() 方法,如果該對象重寫了 finalize() 方法,那么這個對象將會被存入到 F-Queue 隊列中,等待 JVM 的 Finalizer 線程去執行重寫的 finalize() 方法,在這個方法中如果此對象將自己賦值給某個類變量時,則表示此對象已經被引用了。因此不能被標識為死亡狀態,其他情況則會被標識為死亡狀態。
四種引用類型
- 強引用(Strong Reference):強引用是使用最普遍的引用。如果一個對象具有強引用,即便發生OOM那垃圾回收器絕不會回收它。
- 軟引用(Soft Reference):如果一個對象只具有軟引用,則內存空間足夠,垃圾回收器就不會回收它;如果內存空間不足了,就會回收這些對象的內存。軟引用可以和一個引用隊列(ReferenceQueue)聯合使用,如果軟引用所引用的對象被垃圾回收器回收,Java虛擬機就會把這個軟引用加入到與之關聯的引用隊列中。
- 弱引用(Weak Reference):用來描述那些非必須對象, 但是它的強度比軟引用更弱一些, 被弱引用關聯的對象只能生存到下一次垃圾收集發生為止。當垃圾收集器開始工作, 無論當前內存是否足夠, 都會回收掉只 被弱引用關聯的對象。 在JDK 1.2版之后提供了 WeakReference 類來實現弱引用。 弱引用可以和一個引用隊列(ReferenceQueue)聯合使用,如果弱引用所引用的對象被垃圾回收,Java虛擬機就會把這個弱引用加入到與之關聯的引用隊列中。
- 虛引用(Phantom Reference):如果一個對象僅持有虛引用,在任何時候都可能被垃圾回收器回收。虛引用主要用來跟蹤對象被垃圾回收器回收的活動。虛引用必須和引用隊列 (ReferenceQueue)聯合使用。
1.3 標記-清除算法(Mark-Sweep)
標記-清除算法屬於最早的垃圾回收算法,它是由標記階段和清除階段構成的。標記階段會給所有的存活對象做上標記,而清除階段會把沒有被標記的死亡對象進行回收。而標記的判斷方法就是前面講的引用計數算法和可達性分析算法。
問題: 產生內存空間的碎片化,標記-清除算法執行完成之后會產生大量的不連續內存,這樣當程序需要分配一個大對象時,因為沒有足夠的連續內存而導致需要提前觸發一次垃圾回收動作。
1.4 標記-復制算法(Mark-Copy)
將內存分為大小相同的兩塊區域,每次只使用其中的一塊區域,這樣在進行垃圾回收時就可以直接將存活的東西復制到新的內存上,然后再把另一塊內存全部清理掉。
問題:內存的可用率大幅降低:雖然可以解決內存碎片的問題,但因為需要將內存分為大小相同的兩塊內存,那么內存的實際可用量其實只有原來的一半。
1.5 標記-整理算法(Mark-Compact)
由兩個階段組成的:標記階段和整理階段。標記階段和標記-清除算法的標記階段一樣,整理階段不是直接對內存進行清除,而是把所有存活的對象移動到內存的一端,然后把另一端的所有死亡對象全部清除。
2 垃圾回收器
HotSpot 中常使用的垃圾收集器主要包括 7 個:Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS 和 G1(Garbage First)收集器。
2.1 分代收集理論
收集器應該將 Java 堆划分出不同的區域,然后將回收對象依據其年齡(年齡即對象熬過垃圾收集過程的次數) 分配到不同的區域之中存儲。
在 Java 堆划分出不同的區域之后,垃圾收集器可以每次只回收其中某一個或者某些部分的區域,所以有了 Minor GC 、Major GC、Full GC 這樣的回收類型的划分;也能夠針對不同的區域安排與里面存儲對象存亡特征相匹配的垃圾收集算法——因而發展出了“標記-復制算法”“標記-清除算 法”“標記-整理算法”等針對性的垃圾收集算法。
2.2 垃圾收集器分類
- 串行回收器(Serial):串行垃圾回收是為單線程環境設計且只使用一個線程進行垃圾回收,會暫停所有的用戶線程,不適合交互性強的服務器環境
- 並行回收器(Parallel):多個垃圾收集器線程並行工作,同樣會暫停用戶線程,適用於科學計算、大數據后台處理等多交互場景
- 並發回收器(CMS):用戶線程和垃圾回收線程同時執行,不一定是並行的,可能是交替執行,可能一邊垃圾回收,一邊運行應用線程,不需要停頓用戶線程,互聯網應用程序中經常使用,適用對響應時間有要求的場景
- G1回收器:G1垃圾回收器將堆內存分割成不同的區域然后並發地對其進行垃圾回收
-
串行回收器:Serial、Serial Old
-
並行回收器:ParNew、Parallel Scavenge、Parallel Old
-
並發回收器:CMS
-
新生代垃圾收集器:Serial 、 ParNew 、Parallel Scavenge
-
老年代垃圾收集器:Serial Old 、 Parallel Old 、CMS
-
整堆收集器:G1、ZGC、Shenandoah
-
特殊:Epsilon 不進行垃圾回收
組合:
- Serial + Serial Old
- Serial + CMS
- ParNew + Serial Old
- ParNew + CMS
- Parallel Scavenge + Serial Old
- Parallel Scavenge + Parallel Old
- G1
- ZGC
- Epsilon
- Shenandoah
其中: Serial Old 作為 CMS 出現 Concurrent Mode Failure 失敗的后備預案
JDK8 廢棄:Serial + CMS、ParNew + Serial Old
JDK8 默認:Parallel Scavenge + ParallelOld
JDK9 移除:Serial + CMS、ParNew + Serial Old
JDK9 默認:G1
JDK11 新增:Epsilon (實驗)
JDK11 新增:ZGC (實驗)
JDK12 新增:Shenandoah(實驗)
JDK13 更新:ZGC 支持的最大堆大小從 4TB 增加到 16TB
JDK14 更新:ZGC 支持在 Windows 上作為實驗功能
JDK14 棄用:Parallel Scavenge + Parallel Old
JDK14 移除:CMS
JDK15 正式:ZGC 不再標記為實驗功能可以在生產環境中使用
GC性能指標
- 吞吐量:即CPU用於運行用戶代碼的時間與CPU總消耗時間的比值(吞吐量 = 運行用戶代碼時間 / ( 運行用戶代碼時間 + 垃圾收集時間 ))。例如:虛擬機共運行100分鍾,垃圾收集器花掉1分鍾,那么吞吐量就是99%
- 暫停時間:執行垃圾回收時,程序的工作線程被暫停的時間
- 內存占用:Java 堆所占內存的大小
- 收集頻率:垃圾收集的頻次
2.3 Serial 收集器
單線程收集器,“單線程”的意義不僅僅說明它只會使用一個CPU或一個收集線程去完成垃圾收集工作;更重要的是它在垃圾收集的時候,必須暫停其他工作線程(Stop The World),直到垃圾收集完畢;
對於單CPU環境來說,由於Serial收集器沒有線程間的交互,專心做垃圾收集自然可以做獲得最高的垃圾收集效率
使用方式:-XX:+UseSerialGC
(圖源自《深入理解Java虛擬機(第2版)》)
2.4 ParNew 收集器
ParNew 收集器實質上是 Serial 收集器的多線程並行版本,除了同時使用多條線程進行垃圾收集之外,其余的行為包括 Serial 收集器可用的所有控制參數、 收集算法、 Stop The World、 對象分配規則、 回收策略等都與 Serial 收集器完全一致。
ParNew 收集器在單 CPU 服務器上的垃圾收集效率絕對不會比 Serial 收集器高;但是在多 CPU 服務器上,效果會明顯比 Serial 好。
使用方式:-XX:+UseParNewGC
設置線程數: XX:ParllGCThreads
(圖源自《深入理解Java虛擬機(第2版)》)
2.5 Parallel Scavenge 收集器
和 ParNew 收集器類似,是一個新生代收集器。使用復制算法的並行多線程收集器。Parallel Scavenge 是 Java1.8 默認的收集器,特點是並行的多線程回收,以吞吐量(Throughput)優先。適合后台運算,交互不多的任務,如批量處理,訂單處理,科學計算等。
- Parallel Scavenge 收集器的目標是達到一個可控制的吞吐量
- 自適應調節策略:自動指定年輕代、Eden、Suvisor區的比例
使用方式:-XX:+UseParallelGC
分別是控制最大垃圾收集停頓時間: -XX:MaxGCPauseMillis
吞吐量大小 -XX:GCTimeRatio
設置年輕代線程數 XX:ParllGCThreads
自適應調節年輕代、Eden、Suvisor區的比例 -XX:+UseAdaptiveSizePolicy
2.6 Serial Old 收集器
Serial Old是Serial收集器的老年代版本,它同樣是一個單線程收集器,使用標記-整理算法。 這個收集器的主要意義也是供客戶端模式下的 HotSpot 虛擬機使用。
- 在 JDK1.5 及之前,與 Parallel Scavenge 收集器搭配使用(JDK1.6 后有 Parallel Old 收集器可搭配)
- 作為 CMS 收集器的后備預案,在並發收集發生 Concurrent Mode Failure 時使用
使用方式:-XX:+UseSerialGC
另外:Parallel Scavenge 收集器架構中本身有 PS MarkSweep 收集器來進行老年代收集,並非直接調用 Serial Old 收集器,但 PS MarkSweep 收集器與 Serial Old 的實現幾乎是一樣的,所以在官方的許多資料中都是直接以 Serial Old 代替 PS MarkSweep 進行講解。
(圖源自《深入理解Java虛擬機(第2版)》)
2.7 Parallel Old 收集器
Parallel Old 是 Parallel Scavenge 收集器的老年代版本,支持多線程並發收集,基於標記-整理算法實現。
JDK1.6 及之后用來代替老年代的 Serial Old 收集器;(在此之前,如果新生代選擇了 Parallel Scavenge 收集器,老年代除了 Serial Old(PS MarkSweep) 收集器以外別無選擇,其他表現良好的老年代收集器,如 CMS 無法與它配合工作。)
在Server模式,多CPU的情況下;在注重吞吐量以及CPU資源敏感的場景,就有了 Parallel Scavenge 加 Parallel Old 收集器的應用組合;
使用方式:-XX:+UseParallelOldGC
(圖源自《深入理解Java虛擬機(第2版)》)
2.8 CMS 收集器
CMS(concurrent mark sweep)是以獲取最短垃圾收集停頓時間為目標的收集器,CMS 收集器的關注點盡可能縮短垃圾收集時用戶線程的停頓時間,停頓時間越短就越適合與用戶交互的程序驗,CMS 收集器使用的算法是標記-清除算法實現的;
CMS垃圾收集過程
1)初始標記(Initial-Mark)階段:這個階段程序所有的工作線程都將會因為 Stop-the-Wold 機制而出現短暫的的暫停,這個階段的主要任務標記處 GC Roots 能夠關聯到的對象。一旦標記完成后就恢復之前被暫停的的所有應用。 由於直接關聯對象比較小,所以這里的操作速度非常快。
2)並發標記(Concurrent-Mark)階段:從 GC Roots 的直接關聯對象開始遍歷整個對象圖的過程,這個過程耗時較長,但是不需要暫停用戶線程,用戶線程可以與垃圾回收器一起運行。
3)重新標記(Remark)階段:由於並發標記階段,程序的工作線程會和垃圾收集線程同時運行或者交叉運行,因此為了修正並發標記期間因為用戶繼續運行而導致標記產生變動的那一部分對象的標記記錄,這個階段的停頓時間通常比初始標記階段長一些,但也遠比並發標記階段時間短。
4)並發清除(Concurrent-Sweep)階段: 此階段清理刪除掉標記判斷已經死亡的對象,並釋放內存空間。由於不需要移動存活對象,所以這個階段可以與用戶線程同時並發運行。
(圖源自《深入理解Java虛擬機(第2版)》)
並發可達性分析與三色標記(Tri-color Marking)
可達性分析算法理論上要求全過程都基於一個能保障一致性的快照中才能夠進行分析。 垃圾回收器的工作流程大體如下:
- 標記出哪些對象是存活的,哪些是垃圾(可回收);
- 進行回收(清除/復制/整理),如果有移動過對象(復制/整理),還需要更新引用。
三色標記(Tri-color Marking)作為工具來輔助推導,把遍歷對象圖過程中遇到的對象,按照“是否訪問過”這個條件標記成以下三種顏色:
- 白色:尚未訪問過。
- 黑色:本對象已訪問過,而且本對象引用到的其他對象 也全部訪問過了。
- 灰色:本對象已訪問過,但是本對象引用到的其他對象尚未全部訪問完。全部訪問后,會轉換為黑色。
假設現在有白、灰、黑三個集合(表示當前對象的顏色),其遍歷訪問過程為:
- 初始時,所有對象都在 【白色集合】中;
- 將GC Roots 直接引用到的對象 挪到 【灰色集合】中;
- 從灰色集合中獲取對象:
- 將本對象 引用到的 其他對象 全部挪到 【灰色集合】中
- 將本對象 挪到 【黑色集合】里面
- 重復步驟3,直至【灰色集合】為空時結束
- 結束后,仍在【白色集合】的對象即為 GC Roots 不可達,可以進行回收
當 Stop The World 時,對象間的引用是不會發生變化的,可以輕松完成標記。 而當需要支持並發標記時,即標記期間應用線程還在繼續跑,對象間的引用可能發生變化,多標和漏標的情況就有可能發生。
多標-浮動垃圾
假設已經遍歷到E(變為灰色了),此時應用執行了 objD.fieldE = null
:
此刻之后,對象E/F/G是“應該”被回收的。然而因為 E 已經變為灰色了,其仍會被當作存活對象繼續遍歷下去。最終的結果是:這部分對象仍會被標記為存活,即本輪GC不會回收這部分內存。
這部分本應該回收但是沒 有回收到的內存,被稱之為“浮動垃圾”。浮動垃圾並不會影響應用程序的正確性,只是需要等到下一輪垃圾回收中才被清除。
漏標
假設GC線程已經遍歷到E(變為灰色了),此時應用線程先執行了:
var G = objE.fieldG;
objE.fieldG = null; // 灰色E 斷開引用 白色G
objD.fieldG = G; // 黑色D 引用 白色G
此時切回 GC 線程繼續跑,因為 E 已經沒有對 G 的引用了,所以不會將 G 放到灰色集合;盡管因為 D 重新引用了 G,但因為D已經是黑色了,不會再重新做遍歷處理。
最終導致的結果是:G 會一直停留在白色集合中,最后被當作垃圾進行清除。這直接影響到了應用程序的正確性,是不可接受的。
漏標只有同時滿足以下兩個條件時才會發生:
- 灰色對象斷開了白色對象的引用:即灰色對象原來成員變量的引用發生了變化
- 黑色對象重新引用了該白色對象:即黑色對象成員變量增加了新的引用
從代碼的角度看:
var G = objE.fieldG; // 1. 讀取對象 E 的成員變量 fieldG 的引用值(對象 G)
objE.fieldG = null; // 2. 對象 E 往其成員變量 fieldG 寫入 null 值
objD.fieldG = G; // 3. 對象 D 往其成員變量 fieldG 寫入 對象G
只要在上面這三步中的任意一步將對象 G 記錄起來,然后作為灰色對象再進行遍歷即可。
比如放到一個特定的集合,等初始的 GC Roots 遍歷完(並發標記),該集合的對象遍歷即可(重新標記)。
重新標記是需要 STW 的,因為應用程序一直在跑的話,該集合可能會一直增加新的對象,導致永遠都跑不完。當然,並發標記期間也可以將該集合中的大部分先跑了,從而縮短重新標記STW的時間,這個是優化問題了。
CMS收集器三個缺點
- CMS 收集器對 CPU 資源非常敏感。CMS 默認啟動的回收線程數是
(處理器核心數量 + 3) / 4
- CMS 收集器無法處理浮動垃圾,可能出現 Concurrent Mode Failure 失敗而導致另一次 Full GC 的產生
- 空間碎片:CMS是一款基於標記-清除算法實現的收集器,所有會有空間碎片的現象
- 開關參數
-XX:+UseCMS-CompactAtFullCollection
(默認是開啟的,JDK 9開始廢棄),用於在 CMS 收集器不得不進行 Full GC 時開啟內存碎片的合並整理過程,由於這個內存整理必須移動存活對象,是無法並發的 - 另外一個參數
-XX:CMSFullGCsBeforeCompaction
(JDK 9開始廢棄),這個參數的作用是要求 CMS 收集器在執行過若干次(數量由參數值決定) 不整理空間的 Full GC 之后,下一次進入 Full GC 前會先進行碎片整理(默認值為0,表 示每次進入Full GC時都進行碎片整理)。
- 開關參數
2.9 G1 收集器
- G1 GC 圖例來源:Getting Started with the G1 Garbage Collector
- G1 GC 參考資料:Garbage First Garbage Collector Tuning
Garbage First是一款面向服務端應用的垃圾收集器,主要針對配備多核CPU及大容量內存的機器,以極高概率滿足GC停頓時間的同時,還兼具高吞吐量的性能特征。
特點
- G1 把內存划分為多個獨立的區域 Region
- G1 仍然保留分代思想,保留了新生代和老年代,但他們不再是物理隔離,而是一部分Region的集合
- G1 能夠充分利用多CPU、多核環境硬件優勢,盡量縮短 STW 的時間
- G1 整體整體采用標記整理算法,局部是采用復制算法,不會產生內存碎片
- G1 的停頓可預測,能夠明確指定在一個時間段內,消耗在垃圾收集上的時間不超過設置時間
- G1 跟蹤各個Region里面垃圾的價值大小,會維護一個優先列表,每次根據允許的時間來回收價值最大的區域,從而保證在有限事件內高效的收集垃圾
Region區域
G1 不再堅持固定大小以及固定數量的分代區域划分,而是把連續的 Java 堆划分為多個獨立區域(Region),每一個 Region 都可以根據需要扮演新生代的 Eden 空間、 Survivor 空間、老年代空間。
① 使用G1收集器時,它將整個 Java 堆划分成約2048個大小相同的獨立 Region 塊,每個 Region 塊大小根據堆空間的實際大小而定,為2的N次冪,即1MB,2MB,4MB,8MB,16MB,32MB。
② 雖然還保留有新生代和老年代的概念,但新生代和老年代不再是物理隔離的了,它們都是一部分Region (不需要連續)的集合。通過Region的動態分配方式實現邏輯上的連續。
③ G1垃圾收集器還增加了一種新的內存區域,叫做 Humongous 內存區域。主要用於存儲大對象,如果超過 1.5 個 Region,就放到 H 區。一般被視為老年代。
G1 GC過程
G1 提供了兩種 GC 模式,Young GC 和 Mixed GC,兩種均是完全 Stop The World 的。
- Young GC:選定所有年輕代里的 Region,通過控制年輕代的 Region 個數,即年輕代內存大小,來控制 Young GC 的時間開銷。
- Mixed GC:選定所有年輕代里的 Region,外加根據 global concurrent marking 統計得出收集收益高的若干老年代 Region。在用戶指定的開銷目標范圍內盡可能選擇收益高的老年代 Region。
初始標記(Initial Mark,STW):和 CMS 一樣只標記 GC Roots 直接關聯的對象
並發標記(Concurrent Mark):進行 GC Roots Traceing 過程
最終標記(Remark,STW):修正並發標記期間,因程序運行導致發生變化的那一部分對象
篩選回收(Cleanup,STW):根據時間來進行價值最大化收集
(圖源自《深入理解Java虛擬機(第2版)》)
G1 Young GC
- 執行 YoungGC 前:堆分為大約2000個區域。最小大小為 1Mb,最大大小為 32Mb。藍色區域保存老年代對象,綠色區域保存年輕對象。
- 執行 YoungGC 時:將存活的對象(即復制或移動)到一個或多個幸存者區域。如果滿足老化閾值,則某些對象將被提升到老年代區域。
- 執行 YoungGC 后:最近升級的對象以深藍色顯示。幸存者區域為綠色。
總結:
- 堆是單個內存空間,分為多個區域。
- 年輕代內存由一組非連續區域組成。
- 年輕一代的垃圾收集器或年輕的 GC 出現 STW 。將停止所有應用程序線程以進行操作。
- 年輕的 GC 使用多個線程並行完成。
- 將活動對象復制到新的幸存者或老年代的地區。
G1 Mix GC
- 初始標記階段(Initial Marking,STW):存活的對象的初始標記背負在年輕的垃圾收集器上。在日志中,此標記為 GC pause (young)(inital-mark) 。
- 並發標記階段(Concurrent Marking):如果找到空白區域(如“ X”所示),則在 Remark 階段將其立即刪除。另外,計算確定活躍度的信息。
- 最終標記階段(Remark,STW):空區域將被刪除並回收。現在可以計算所有區域的區域活躍度。
- 篩選回收階段/復制清理階段(Copying/Cleanup,STW): G1選擇“活度”最低的區域,這些區域可以被最快地收集。然后與年輕的GC同時收集這些區域。這在日志中表示為[GC pause (mixed)] 。因此,年輕代和老年代都是同時收集的。
- 篩選回收階段-(復制/清理)階段之后:選定的區域已被收集並壓縮為圖中所示的深藍色區域和深綠色區域。
總結
- 並發標記階段
- 活動信息是在應用程序運行時同時計算的。
- 該活動信息標識在疏散暫停期間最適合回收的區域。
- 像 CMS 中沒有清掃階段。
- 最終標記階段
- 使用開始快照(SATB)算法,該算法比 CMS 使用的算法快得多。
- 完全回收空區域。
- 篩選回收階段
- 同時回收年輕一代和老一代。
- 老年代地區是根據其活躍度來選擇的
2.10 ZGC、Epsilon、Shenandoah
Epsilon
JDK11 新增的GC,一個處理內存分配但不實現任何實際內存回收機制的GC,一旦可用的Java堆耗盡,JVM將關閉。如果有 System.gc()
調用,實際上什么也不會發生, 因為沒有內存回收,這個實現可能會警告用戶嘗試強制GC是徒勞。
使用方式:-XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC
用途:
- 性能測試(它可以幫助過濾掉 GC 引起的性能假象)
- 內存壓力測試(例如知道測試用例應該分配不超過 1GB 的內存,我們可以使用
-XX:+UnlockExperimentalVMOptions –XX:+UseEpsilonGC -Xmx1g
,如果程序有問題則程序會崩潰) - 非常短的 JOB 任務(GC 清理可能會讓時間更長)
- VM 接口測試
- Last-drop 延遲&吞吐改進
ZGC
ZGC的設計目標是:支持 TB 級內存容量,暫停時間低(<10ms),對整個程序吞吐量的影響小於15%。 將來還可以擴展實現機制,以支持不少令人興奮的功能,例如多層堆(即熱對象置於 DRAM 和冷對象置於 NVMe 閃存),或壓縮堆。
- JDK11 新增:ZGC (實驗)
-XX:+UnlockExperimentalVMOptions -XX:+UseZGC
- JDK13 更新:ZGC 支持的最大堆大小從 4TB 增加到 16TB
- JDK14 更新:ZGC 支持在 Windows 上作為實驗功能
- JDK15 正式:ZGC 不再標記為實驗功能可以在生產環境中使用
-XX:+UseZGC
Shenandoah
JDK12 新增的GC,低暫停時間垃圾收集器(實驗性)Shenandoah的暫停時間與堆大小無關,這意味着無論堆是200MB還是200GB,都將具有相同的一致暫停時間。它的 evacuation 階段工作能通過與正在運行中 Java 工作線程同時進行(即並發,concurrent),從而減少 GC 的停頓時間。
使用方法: -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC
某些 JDK 會在 build 時通過 --with-jvm-features=-shenandoahgc
來禁用 Shenandoah。
內容為之前學習筆記整理,如果有問題請指正!(引用圖片已標記來源)