本文儘可能對GC的情況進行簡要表述,目標是用最少的描述來完整闡明JDK8 Java Hotspot VM目前支持的各種GC方式及其調優方法。 為精簡行文內容,正文中不包含GC觀測手段的介紹,讀者可以參考 [Java Platform, Standard Edition HotSpot 虛擬機垃圾收集... ...
參考文檔
- Java Virtual Machine Technology (JDK8官方)
- Java Platform, Standard Edition HotSpot 虛擬機垃圾收集調優指南 (JDK8官方)
- Java平臺標準版工具參考(unix) (JDK8官方)
- 阿裡巴巴Dragonwell8用戶指南 (Dragonwell官方)
- Java性能優化之JVM GC(垃圾回收機制)(大鵝coding)
目錄
前言
本文儘可能對GC的情況進行簡要表述,目標是用最少的描述來完整闡明JDK8 Java Hotspot VM目前支持的各種GC方式及其調優方法。
為精簡行文內容,正文中不包含GC觀測手段的介紹,讀者可以參考 Java Platform, Standard Edition HotSpot 虛擬機垃圾收集調優指南 部分獲得更多相關信息。
省流
預設收集器
收集器的預設配置通常遵循如下原則:
- 兩個以上CPU 且 2GB以上的物理記憶體時,預設使用並行收集器
- 否則預設使用串列收集器
下麵提供一個簡單的表格:
平臺 | 操作系統 | 預設收集器類型 |
---|---|---|
i586 | Linux | 並行收集器 |
i586 | Windows | 串列收集器 |
SPARC (64-bit) | Solaris | 並行收集器 |
AMD (64-bit) | Linux | 並行收集器 |
AMD (64-bit) | Windows | 並行收集器 |
預設堆大小
參數 | 客戶端 | 服務端 |
---|---|---|
-Xmx 最大堆大小 |
物理記憶體小於等於192M時,為物理記憶體的一半; 否則為物理記憶體的1/4,但不超過256M |
物理記憶體小於等於192M時,為物理記憶體的一半; 否則為物理記憶體的1/4,32位JVM不超過1G,64位JVM不超過32GB |
-Xms 初始堆大小 |
物理記憶體的1/64,但不低於8M,不超過256M | 物理記憶體的1/64,但不低於8M,不超過256M |
-Xmn 新生代空間 |
總堆大小的1/3 | 總堆大小的1/3 |
調優策略
- 儘量不要指定最大堆值(除非你知道最優質值),這樣的話堆會自行增長/減小到能滿足吞吐量目標的大小。
- 如果最大堆值無法滿足你需要的吞吐量目標,則可以考慮將最大堆值大小設置為接近總物理記憶體的值,但需要保證它不會導致使用到swap區域(如果仍然無法滿足吞吐量目標那就要考慮增加物理記憶體了)。
- 若配置了最大停頓時間目標,則可能會導致吞吐量目標無法達到,所以要註意選擇一個折衷值。
- 實現吞吐量目標需要更大的堆,實現最大暫停時間目標和最小占用空間目標需要更小的堆。
收集器選用原則
- 應用程式數據集大小
- 小於等於100M:串列收集器
- 其他情況:並行收集器 或 CMS 或 G1
基本定義
調優目標
所有的GC調優主要圍繞如下四個目標進行
- 停頓時間 暫停時間是垃圾收集器停止應用程式並回收不再使用的空間的持續時間。
- 吞吐量 可以理解為非垃圾收集的時間的占比。吞吐量目標是根據收集垃圾所花費的時間和在垃圾收集之外所花費的時間(稱為 應用程式時間 )來衡量的。目標由命令行選項
-XX:GCTimeRatio=<nnn>
指定。垃圾收集時間與應用程式時間的比率為 1 / (1 +<nnn>
)。 例如,-XX:GCTimeRatio=19
將目標設置為垃圾收集總時間的 1/20 或 5%。 - Footprint占用空間 可以理解為堆的占用空間。
- Promptness敏捷 對象死亡和記憶體變為可用之間的時間。
收集器分類
- 串列收集器 使用單個線程來執行所有垃圾收集工作,這使得它相對高效,因為線程之間沒有通信開銷。 它最適合單處理器機器,因為它不能利用多處理器硬體,儘管它在多處理器上對於具有小數據集(最多大約 100 MB)的應用程式很有用。 在某些硬體和操作系統配置中預設選擇串列收集器,或者可以使用選項
-XX:+UseSerialGC
顯式啟用。 - 並行收集器(也稱為 吞吐量收集器 ) 並行執行次要收集,這可以顯著減少垃圾收集開銷。 它適用於在多處理器或多線程硬體上運行的具有中型到大型數據集的應用程式。 並行收集器在某些硬體和操作系統配置上預設選擇,或者可以使用選項
-XX:+UseParallelGC
顯式啟用。- 並行壓縮是一種使並行收集器能夠並行執行主要收集的功能。 如果沒有並行壓縮,主要的收集是使用單個線程執行的,這會顯著限制可伸縮性。 如果指定了選項
-XX:+UseParallelGC
,則預設啟用並行壓縮。 關閉它的選項是-XX:-UseParallelOldGC
。
- 並行壓縮是一種使並行收集器能夠並行執行主要收集的功能。 如果沒有並行壓縮,主要的收集是使用單個線程執行的,這會顯著限制可伸縮性。 如果指定了選項
- 大多數併發收集器 併發執行其大部分工作(例如,當應用程式仍在運行時)以保持垃圾收集暫停時間短。 它專為具有中型到大型數據集的應用程式而設計,在這些應用程式中響應時間比總吞吐量更重要,因為用於最小化暫停的技術會降低應用程式性能。 Java HotSpot VM 提供了兩個主要併發的收集器之間的選擇。使用選項
-XX:+UseConcMarkSweepGC
啟用 CMS 收集器或使用選項-XX:+UseG1GC
啟用 G1 收集器。- 併發標記掃描收集器(Concurrent Mark Sweep Collector, CMS) 此收集器適用於那些喜歡較短垃圾收集暫停時間並且能夠與垃圾收集共用處理器資源的應用程式。
- 垃圾優先垃圾收集器(Garbage-First Garbage Collector, G1) 這個伺服器風格的收集器適用於具有大記憶體的多處理器機器。它在實現高吞吐量的同時,高概率地滿足垃圾收集暫停時間目標。
記憶體中代的排列
預設排列
顯示預設的世代安排(用於除並行收集器和 G1 之外的所有收集器):
- Eden: 絕大多數對象最初在伊甸園中分配。
- Survivor x 2: 一個幸存者空間在任何時候都是空的,作為伊甸園中任何活物體的歸宿; 另一個幸存者空間是下一次複製收集期間的目的地。 對象以這種方式在幸存者空間之間複製,直到它們足夠老可以被永久使用(複製到永久代)。
- Virtual: 在虛擬機初始化時,為堆保留整個空間。 保留空間的大小可以使用
-Xmx
選項指定。 如果-Xms
參數的值小於-Xmx
參數的值,那麼並非所有保留的空間都會立即提交給虛擬機。 未提交的空間在此圖中標記為“虛擬”。 堆的不同部分(tenured generation 和 young generation)可以根據需要增長到虛擬空間的極限。 - Tenured: 永久代空間。該空間用滿時會觸發 Full GC。
並行收集器的排列
各個空間的使用方式與 預設排列 中的一樣。
G1收集器的排列
- 把堆區劃分成很多個大小相同的區域(Region),新、老年代也不再固定在某個區域,每一個Region都可以根據運行情況的需要,扮演Eden、Survivor、老年代區域、或者Humongous區域。
- 大對象會被存儲到Humongous區域,G1大多數情況下會把這個區域當作老年代來看待。如果對象占用空間超過Region的容量,就會存放到N個連續的 Humongous Region 中。
回收演算法
僅圖示,各演算法的描述可以參考 《Java性能優化之JVM GC(垃圾回收機制)- 大鵝coding》
標記-清除 演算法
標記-複製 演算法
註意,從Survivor如果已滿,則也會使用“標記-複製”演算法將對象回收到老年代區域。
標記-整理 演算法
通常用於老年代的回收。
串列收集器
步驟大致如下:
- 各個線程運行到安全點,暫停應用程式
- 使用“標記-複製”演算法,並清理Eden空間和Survivor空間
- 使用“標記-整理”演算法,並清理老年代空間
- 如果沒有滿足MinHeapFreeRatio和MaxHeapFreeRatio目標,則擴展對應空間
- 恢復應用程式執行
並行收集器
目標優先順序
這些目標按以下順序處理:
- 最大停頓時間目標
- 吞吐量目標
- 最小占用空間目標
首先滿足最大暫停時間目標。 只有在滿足它之後,吞吐量目標才會得到解決。 同樣,只有在滿足前兩個目標後,才會考慮footprint占用空間目標。
GC步驟
步驟與串列收集器基本一直,主要區別為並行收集器的垃圾收集過程使用多線程處理。
併發標記掃描(CMS)收集器
目標優先順序 與並行收集器相同。
GC步驟(僅老年代)
- 年輕代收集與老年代的收集是相互獨立的。
- 老年代收集的步驟如下(“標記-清除”演算法):
- 各個線程運行到安全點,暫停應用程式(Stop Tow world)
- 進入初始標記階段,標記所有與根節點直接關聯的對象(可多線程)
- 恢復應用程式,進入併發標記階段
- (一個處理器)跟蹤和標記新增對象
- (一個或多個處理器)遍歷2中對象的可訪問對象圖
- 暫停應用(Stop Tow world),返回不可達的對象清單
- 恢復應用,(一個處理器)清除無法訪問對象(“標記-清除”演算法),重新計算和調整堆大小。
- 如果出現“併發模式故障”,或老年代空間不足的情況,則會觸發STW和使用”併發-整理“演算法的Full GC。
i-cms 增量模式
在只有一或兩個處理器的伺服器上,由於在併發標記階段始終需要有處理器來執行標記過程,因此可能會造成過大的應用中斷。開啟增量模式可以使用“占空比”來使CMS收集器自願放棄部分工作量,從而減少垃圾收集所占用的應用程式CPU時間片。
垃圾優先(G1)垃圾收集器
目標優先順序 與並行收集器相同。
GC步驟
- 各個線程運行到安全點,暫停應用程式(Stop Tow world)
- 進入“初始標記階段”,標記根節點能夠直接關聯的對象及 Survivor Region 。
- 恢復應用程式
- 跟蹤和標記新增對象(可多線程)
- 查找2中對象的可達對象圖(可多線程)
- 遍歷與2中Survivor Region關聯的卡表信息,找到被老年代所引用的對象(可多線程)
- 暫停應用程式(Stop Tow world)
- 根據以往的暫停情況預估此次要回收的區域數量(單線程)
- 使用“標記-複製”演算法清理指定區域的空間(可多線程)
- 如果空間不足,則觸發Full GC(“標記-整理“演算法)
- 恢復應用程式
註意,G1收集器的排列 可以參考前文
JVM重要調優參數
本節只列出部分相對重要的參數,全部可以參數描述可以參考 Java平臺標準版工具參考(unix)。
通用參數
-
-XX:+DisableExplicitGC
啟用禁用處理對System.gc()
調用的選項。 預設情況下禁用此選項,這意味著處理對System.gc()
的調用。 如果禁用對 System.gc() 調用的處理,JVM 仍會在必要時執行 GC。 -
-XX:InitialHeapSize=size
設置記憶體分配池的初始大小(以位元組為單位)。 此值必須為 0 或 1024 的倍數且大於 1 MB。 附加字母“k”或“K”表示千位元組,“m”或“M”表示兆位元組,“g”或“G”表示千兆位元組。 預設值是在運行時根據系統配置選擇的。請註意,
-Xms
選項設置堆的最小堆大小和初始堆大小。 如果在命令行中的-XX:InitialHeapSize
之後出現-Xms
,則初始堆大小將設置為使用-Xms
指定的值。 -
-XX:MaxGCPauseMillis=time
設置最大 GC 暫停時間的目標(以毫秒為單位)。 這是一個軟目標,JVM 將盡最大努力實現它。 預設情況下,沒有最大暫停時間值。 -
-XX:MaxHeapSize=size
設置記憶體分配池的最大大小(以位元組為單位)。 此值必須是 1024 的倍數且大於 2 MB。 附加字母“k”或“K”表示千位元組,“m”或“M”表示兆位元組,“g”或“G”表示千兆位元組。 預設值是在運行時根據系統配置選擇的。 對於伺服器部署,-XX:InitialHeapSize
和-XX:MaxHeapSize
通常設置為相同的值。-XX:MaxHeapSize
選項等同於-Xmx
。 -
-XX:MaxHeapFreeRatio=percent
設置 GC 事件後可用堆空間的最大允許百分比(0 到 100)。 如果可用堆空間擴展到該值以上,則堆將收縮。 預設情況下,此值設置為 70%。 -
-XX:MaxMetaspaceSize=size
設置可以分配給類元數據的最大本機記憶體量。 預設情況下,大小不受限制。 應用程式的元數據量取決於應用程式本身、其他正在運行的應用程式以及系統上可用的記憶體量。 -
-XX:MaxNewSize=size
為年輕一代(苗圃)設置堆的最大大小(以位元組為單位)。 預設值是根據人體工程學設置的。 -
-XX:MaxRAMPercentage=percent
將 JVM 在應用人體工程學試探法之前可用於 Java 堆的最大記憶體量設置為最大記憶體量的百分比,如-XX:MaxRAM
選項中所述。 預設值為 25%。如果此選項和影響最大記憶體量的其他選項的組合結果大於壓縮 oops 可定址的記憶體範圍,則指定此選項將禁用壓縮 oops 的自動使用。 有關壓縮 oops 的更多信息,請參閱
-XX:UseCompressedOops
。 -
-XX:MaxTenuringThreshold=threshold
設置用於自適應 GC 大小調整的最大使用期閾值。 最大值為 15。並行(吞吐量)收集器的預設值為 15,CMS 收集器的預設值為 6。 -
-XX:MetaspaceSize=size
設置分配的類元數據空間的大小,該空間將在第一次超出時觸發垃圾回收。 垃圾回收的閾值根據使用的元數據量增加或減少。 預設大小取決於平臺。 -
-XX:MinHeapFreeRatio=percent
設置 GC 事件後可用堆空間的最小允許百分比(0 到 100)。 如果可用堆空間低於此值,則將擴展堆。 預設情況下,此值設置為 40%。 -
-XX:MinRAMPercentage=percent
將 JVM 在應用人體工程學試探法之前可用於 Java 堆的最大記憶體量設置為最大記憶體量的百分比,如針對小型堆的-XX:MaxRAM
選項中所述。 小堆是大約 125 MB 的堆。 預設值為 50%。 -
-XX:NewRatio=ratio
設置年輕代和老年代大小之間的比例。 預設情況下,此選項設置為 2。以下示例顯示如何將年輕/年老比率設置為 1: -
-XX:NewSize=size
為年輕一代(苗圃)設置堆的初始大小(以位元組為單位)。 附加字母“k”或“K”表示千位元組,“m”或“M”表示兆位元組,“g”或“G”表示千兆位元組。堆的年輕代區域用於新對象。 GC 在這個區域比在其他區域更頻繁地執行。 如果年輕代的大小太小,那麼將會執行大量的次要 GC。 如果大小太高,則只會執行完整的 GC,這可能需要很長時間才能完成。 Oracle 建議您將新生代的大小保持在整個堆大小的一半到四分之一之間。
-XX:NewSize
選項等同於-Xmn
。 -
-XX:+PrintGC
在每次 GC 時啟用消息列印。 預設情況下,此選項被禁用。 -
-XX:+PrintGCDateStamps
啟用在每個 GC 上列印日期戳。 預設情況下,此選項被禁用。 -
-XX:+PrintGCDetails
允許在每次 GC 時列印詳細消息。 預設情況下,此選項被禁用。 -
-XX:+PrintGCTaskTimeStamps
為每個單獨的 GC 工作線程任務啟用時間戳列印。 預設情況下,此選項被禁用。 -
-XX:+PrintGCTimeStamps
在每個 GC 上啟用時間戳列印。 預設情況下,此選項被禁用。 -
-XX:+ScavengeBeforeFullGC
在每次完整 GC 之前啟用年輕代的 GC。 預設情況下啟用此選項。 Oracle 建議您_不要_ 禁用它,因為在 full GC 之前清除年輕代會減少從老年代空間到達年輕代空間的對象數量。 要在每次完整 GC 之前禁用年輕代的 GC,請指定 -XX:-ScavengeBeforeFullGC。 -
-XX:SurvivorRatio=ratio
設置伊甸園空間大小和幸存者空間大小之間的比率。 預設情況下,此選項設置為 8。以下示例顯示如何將 eden/survivor 空間比率設置為 4:
-XX:SurvivorRatio=4
-
-XX:+UseGCOverheadLimit
在拋出OutOfMemoryError
異常之前,允許使用限制 JVM 在 GC 上花費的時間比例的策略。 預設情況下啟用此選項,如果超過 98% 的總時間花在垃圾收集上並且只有不到 2% 的堆被回收,則並行 GC 將拋出一個OutOfMemoryError
。 當堆很小時,可以使用此功能來防止應用程式長時間運行而沒有任何進展。 要禁用此選項,請指定 -XX:-UseGCOverheadLimit。
串列收集器
- -XX:+UseSerialGC
啟用串列垃圾收集器的使用。 這通常是不需要垃圾收集的任何特殊功能的小型和簡單應用程式的最佳選擇。 預設情況下,這個選項是禁用的,收集器是根據機器的配置和 JVM 的類型自動選擇的。
並行收集器
-
-XX:+UseParallelGC
啟用並行清除垃圾收集器(也稱為吞吐量收集器)以通過利用多個處理器來提高應用程式的性能。預設情況下,這個選項是禁用的,收集器是根據機器的配置和 JVM 的類型自動選擇的。 如果啟用,則
-XX:+UseParallelOldGC
選項會自動啟用,除非您明確禁用它。 -
-XX:+UseParallelOldGC
啟用對完整 GC 的並行垃圾收集器的使用。 預設情況下,此選項被禁用。 啟用它會自動啟用-XX:+UseParallelGC
選項。 -
-XX:+UseParNewGC
啟用在年輕代中使用並行線程進行收集。 預設情況下,此選項被禁用。 當您設置 -XX:+UseConcMarkSweepGC 選項時,它會自動啟用。 在 JDK 8 中棄用了不使用-XX:+UseConcMarkSweepGC
選項的-XX:+UseParNewGC
選項。 -
-XX:InitialSurvivorRatio=ratio
設置吞吐量垃圾收集器使用的初始幸存者空間比率(通過 -XX:+UseParallelGC 和/或 -XX:+UseParallelOldGC 選項啟用)。 預設情況下,吞吐量垃圾收集器通過使用 -XX:+UseParallelGC 和 -XX:+UseParallelOldGC 選項啟用自適應大小調整,並且根據應用程式行為調整幸存者空間的大小,從初始值開始。 如果禁用自適應大小調整(使用 -XX:-UseAdaptiveSizePolicy 選項),則應使用 -XX:SurvivorRatio 選項為應用程式的整個執行設置幸存者空間的大小。根據年輕代的大小(Y)和初始幸存者空間比例(R),可以使用以下公式計算幸存者空間的初始大小(S):
S=Y/(R+2)
等式中的 2 表示兩個幸存者空間。 指定為初始幸存者空間比例的值越大,初始幸存者空間大小越小。
預設情況下,初始survivor空間比例設置為8。如果新生代空間大小使用預設值(2MB),則survivor空間的初始大小將為0.2MB。
-
-XX:ParallelGCThreads=threads
設置用於年輕代和老年代並行垃圾回收的線程數。 預設值取決於 JVM 可用的 CPU 數量。 -
-XX:+ParallelRefProcEnabled
啟用並行引用處理。 預設情況下,此選項被禁用。
大多數併發收集器
-
-XX:ConcGCThreads=threads
設置用於併發 GC 的線程數。 預設值取決於 JVM 可用的 CPU 數量。 -
-XX:InitiatingHeapOccupancyPercent=percent
設置開始併發 GC 周期的堆占用百分比(0 到 100)。 它被垃圾收集器使用,根據整個堆的占用情況觸發併發 GC 周期,而不僅僅是其中一個代(例如,G1 垃圾收集器)。
CMS
-
-XX:+UseConcMarkSweepGC
為老年代啟用 CMS 垃圾收集器。 當吞吐量 (-XX:+UseParallelGC
) 垃圾收集器無法滿足應用程式延遲要求時,Oracle 建議您使用 CMS 垃圾收集器。 G1 垃圾收集器 (-XX:+UseG1GC
) 是另一種選擇。預設情況下,這個選項是禁用的,收集器是根據機器的配置和 JVM 的類型自動選擇的。 啟用此選項後,將自動設置
-XX:+UseParNewGC
選項,您不應禁用它,因為以下選項組合已在 JDK 8 中棄用:-XX:+UseConcMarkSweepGC -XX:-UseParNewGC
. -
-XX:+CMSClassUnloadingEnabled
使用併發標記清除 (CMS) 垃圾收集器時啟用類卸載。 預設情況下啟用此選項。 要禁用 CMS 垃圾收集器的類卸載,請指定 -XX:-CMSClassUnloadingEnabled。 -
-XX:CMSExpAvgFactor=percent
設置在計算併發收集統計信息的指數平均值時用於對當前樣本加權的時間百分比(0 到 100)。 -
-XX:CMSInitiatingOccupancyFraction=percent
設置開始 CMS 收集周期的老年代占用百分比(0 到 100)。 預設值設置為 -1。 任何負值(包括預設值)都意味著“-XX:CMSTriggerRatio”用於定義初始占用率的值。 -
-XX:+CMSScavengeBeforeRemark
在 CMS 標記步驟之前啟用清理嘗試。 預設情況下,此選項被禁用。 -
-XX:CMSTriggerRatio=percent
設置在 CMS 收集周期開始之前分配的由 -XX:MinHeapFreeRatio 指定的值的百分比(0 到 100)。 預設值設置為 80%。 -
-XX:+ExplicitGCInvokesConcurrent
通過使用System.gc()
請求啟用併發 GC 調用。 預設情況下禁用此選項,只能與-XX:+UseConcMarkSweepGC
選項一起啟用。 -
-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses
通過使用System.gc()
請求併在併發 GC 周期期間卸載類來啟用併發 GC 調用。 預設情況下禁用此選項,只能與-XX:+UseConcMarkSweepGC
選項一起啟用。 -
-XX:+UseCMSInitiatingOccupancyOnly
允許使用占用值作為啟動 CMS 收集器的唯一標準。 預設情況下,此選項被禁用,可以使用其他標準。
G1
-
-XX:+UseG1GC
啟用垃圾優先 (G1) 垃圾收集器的使用。 它是一種伺服器風格的垃圾收集器,針對具有大量 RAM 的多處理器機器。 它很有可能滿足 GC 暫停時間目標,同時保持良好的吞吐量。 建議將 G1 收集器用於需要大堆(大小約為 6 GB 或更大)且 GC 延遲要求有限(穩定且可預測的暫停時間低於 0.5 秒)的應用程式。預設情況下,這個選項是禁用的,收集器是根據機器的配置和 JVM 的類型自動選擇的。
-
-XX:G1HeapRegionSize=size
設置使用垃圾優先 (G1) 收集器時 Java 堆被細分的區域大小。 該值可以介於 1 MB 和 32 MB 之間。 預設區域大小是根據堆大小根據人體工程學確定的。 -
-XX:+G1PrintHeapRegions
啟用有關哪些區域已分配以及哪些區域已由 G1 收集器回收的信息的列印。 預設情況下,此選項被禁用。 -
-XX:G1ReservePercent=percent
將保留的堆百分比(0 到 50)設置為假上限,以減少 G1 收集器提升失敗的可能性。 預設情況下,此選項設置為 10%。 -
-XX:+UseStringDeduplication
啟用字元串去重。 預設情況下,此選項被禁用。 要使用此選項,您必須啟用垃圾優先 (G1) 垃圾收集器。 請參閱-XX:+UseG1GC
選項。