基礎篇:java GC 總結,建議收藏
- 垃圾標記算法
- 垃圾回收算法
- major gc、mini gc、full gc、mixed gc 又是什麼,怎麼觸發的
- 垃圾回收器的介紹
- Safe Point 和 Safe Region
- 什麼是 TLAB 和 PLAB ?
- CMS、G1 新生代的 GC 如何避免全堆掃描
- CMS 和 G1 為了防止並發時的漏標分別用了什麼手段
- 什麼是 logging write barrier
- CMS 常見問題
- GC 事件和日誌分析
- JVM 常用參數匯總
關注公眾號,一起交流:潛行前行
1 垃圾標記算法
引用計算法
- 引用計數法是最簡單有效的垃圾標記方法,它會把對象被引用的次數記錄下來,當被引用時,計數加一。當其他變量不再指向目標對象時,則引用減一。對象引用數為零時 ,則可以進行內存回收釋放
- 無法解決循環引用問題
根可達性分析
- 從 GC Root 開始進行對象搜索,可以被搜索到的對象即為可達對象,不可達對象便可以作為垃圾被回收掉。目前 Java 中主流的虛擬機均採用此算法
- 在Java語言里,可作為GC Roots的對象包括下面幾種:
- 虛擬機棧(棧幀中的本地變量表)中的引用的對象
- 方法區中的類靜態屬性引用的對象
- 方法區中的常量引用的對象
- 本地方法棧中JNI(即一般說的Native方法)的引用的對象
2 垃圾回收算法
- 複製:將一塊內存區域進行對半分,當一半的內存使用完時,便將其中存活的對象複製到另一半內存區域中,原先的區域進行回收。不存在內存碎片問題,實現簡單運行高效,但是有個缺點,就是對內存的利用率只有 50%
- 標記清除:算法分為 「標記」 和 「清理」兩個階段
- 標記階段:標記出所有需要回收的對象
- 清除階段:標記完成後,統一清除回收被標記的對象
- 由於對象之前在內存中的分佈是無規律的,標記清除算法會產生大量不連續的內存碎片,造成連續的大內存空間缺失,阻礙大內存對象的分配,嚴重時會觸發垃圾回收,甚至出現 OutOfMemeryError。而且如果大部分的對象是朝生夕死的,標記的對象就會更多,效率更低
- 標記整理:步驟與 標記清除步 驟一致,但它的第二步是整理標記之外的所有對象,將所有對象向前移動,之後直接回收掉存活對象之外的內存區域。標記整理 不會存在內存碎片,但是效率是偏低的
按年代劃分-分代算法
- 新生代回收(Minor GC/Young GC):指只是進行新生代的回收
- 老年代回收(Major GC/Old GC):指只是進行老年代的回收。目前只有 CMS 垃圾回收器會有這個單獨的回收老年代的行為
- 整堆回收(Full GC):收集整個堆,包括年輕代、老年代,如果有永久代的話還包括永久代
3 major gc、mini gc、mixed gc又是什麼,怎麼觸發的?
- major gc:個人理解應該是指 old gc。不過有些人認為和 full gc 等價
- 執行 System.gc()、jmap -dump 等命令會觸發 full gc
- 有永久代的話,永久代滿了也會觸發 full gc
- 大對象直接在老年代申請分配,如果此時老年代空間不足則會觸發 full gc
- 新生代對象 gc 年齡到達閾值需要晉陞,老年代如果放不下的話會觸發 full gc
- mini gc:指的也是年輕代的 young gc
- 年輕代的 eden 快要被佔滿的時候會觸發 young gc
- eden 快滿的觸發因素有兩個,一個是為對象分配內存不夠,一個是為 TLAB 分配內存不夠
- mixed gc:這個是 G1 收集器特有的,指的是收集整個年輕代和部分老年代的 GC
- 在 young gc 之後,當老年代的堆佔有率達到參數 (-XX:InitiatingHeapOccupancyPercent) 設定的值時則觸發 mixed GC
4 垃圾回收器的介紹
Serial New 和 Serial Old
- jvm 誕生初期所採用的垃圾回收器,單線程,獨佔式,適合單 CPU
- 單線程進行垃圾回收時,必須暫停所有的工作線程,直到它回收結束。這個暫停稱之為
Stop The World
,但是 STW 會帶來差的性能影響
Parallel Scavenge 和 Parallel Old
- 為了提高 jvm 的回收效率,jvm 使用了多線程的垃圾回收器,關注吞吐量的垃圾回收器,可以更高效的利用CPU 時間,從而儘快完成程序的運算任務
- Parallel Scavenge 收集器提供了兩個參數用於精確控制吞吐量,可以分別是控制最大垃圾收集停頓時間
- -XX:MaxGCPauseMillis參數以及直接設置吞吐量大小的
- -XX:GCTimeRatio參數。其也經常被稱為「吞吐量優先」收集器
ParNew 和 CMS
- ParNew 與 Parallel Scavenge 差不多。區別是 Parallel Scavenge 是一個可控制的吞吐量並行垃圾回收器,ParNew 沒有參數來控制吞吐量和停頓時間
- ParNew 可以和 CMS 搭配使用,而 Parallel Scavenge 不可用,其根本原因是設計上就是沒想過兼容 CMS
- CMS(Concurrent Mark Sweep) 是一款針對老年代的垃圾回收器,追求最短的回收停頓時間(STW)為目標,採用的是
標記-清除
算法
CMS 的回收流程
- 初始標記:只標記與 GC Root 有直接關聯的對象,這類的對象比較少,標記快。需要 STW
- 並發標記:並發標記與初始化標記的對象有關聯的所有對象,這類的對象比較多所以採用的並發,與用戶線程一起跑
- 並發預清理(Concurrent Preclean):並發標記階段是與應用線程並發執行的,有些引用關係已經發生改變,通過卡片標記(Card Marking),如果引用關係發生改變,JVM會將發生改變的區域標記位「臟區」(Dirty Card),然後在本階段,這些臟區會被找出來,刷新引用關係,清除「臟區」標記
- 並發可取消的預清理(Concurrent Abortable Preclean):和並發預清理階段工作差不多,用來減少Final Remark 階段的暫停時間。該階段會不斷循環處理:標記老年代的可達對象、掃描處理Dirty Card區域中的對象引用關係。循環中斷條件
- 達到循環次數
- 達到循環執行時間閾值
- 新生代內存使用率達到閾值
- 最終標記 (Final Remark):修正並發標記時候標記產生異動的對象標記,這塊的時間比初始標記稍長一些,但是比起並發標記要快很多。需要 STW
- 遍歷新生代對象,重新標記
- 根據GC Roots,重新標記
- 遍歷老年代的Dirty Card,重新標記
- 並發清除(Concurrent Sweep):與用戶線程一起運行,進行對象回收清除
- 缺點
- 浮動垃圾:在CMS進行並發清除階段,GC線程是並發的,所以在清除的時候用戶線程會產出新的垃圾。 因此在進行回收時需要預留一部分的空間來存放這些新產生垃圾(JDK 1.6 設置的閾值為92%)。但是如果用戶線程產出的垃圾比較快,預留內存放不下的時候就會出現 Concurrent Mode Failure,這時虛擬機將臨時啟用 Serial Old 來替代 CMS。
- 內存碎片:因為採用的是 標記-清除 算法,會產生內存碎片
G1
- G1 垃圾回收器的設計思想與上面的垃圾回收器的都不一樣,前面垃圾回收器採用的都是 分代劃分 的方式進行設計的,而 G1 則是將堆看作是一個整體的區域,這個區域被劃分成了一個個大小一致的獨立區域(Region),而每個區域都可以根據需要成為 Eden、Survivor 以及老年代區域
G1的回收流程
-
初始標記(Initial Marking):標記與 GC Roots 能關聯到的對象,修改 TAMS (Top at Mark Start) 給堆對象拍個快照,這個過程是需要暫停用戶線程的,但是耗時非常的短。需要 STW
- Region 記錄著兩個 top-at-mark-start (TAMS) 指針,分別為
prevTAMS
和nextTAMS
。在 nextTAMS~top的之間對象是新分配的,被視為隱式 marked(存活對象)。對象是否存活使用bitmap
位圖標誌,prevBitmap
記錄第 n-1 輪 concurrent marking 所得的對象存活狀態,nextBitmap
記錄第 n 輪 concurrent marking 的結果 - top 是該 Region 的當前分配指針,[bottom, top) 是當前該 Region 已用的部分,[top, end) 是尚未使用的可分配空間
- [bottom, prevTAMS):這部分里的對象存活信息可以通過
prevBitmap
來得知 - [prevTAMS, nextTAMS):這部分里的對象在第 n-1 輪 concurrent marking 是隱式存活的
- [nextTAMS, top):這部分里的對象在第 n 輪 concurrent marking 是隱式存活的
- Region 記錄著兩個 top-at-mark-start (TAMS) 指針,分別為
-
並發標記(Concurrent Marking):進行掃描標記所有課回收的對象。當掃描完成後,並發會有引用變化的對象,而這些對象會漏標這些漏標的對象會被 SATB 算法所解決
- SATB(snapshot-at-the-beginning):舊對象區域 [bottom, nextTAMS) 按 nextTAMS 生成時的存活快照為準,即對象在 nextTAMS 生成之後變成垃圾也不會被回收
- 如果在並發標記時,引用發生改變的對象將被放入 satb_mark_queue 隊列(寫屏障實現),之後在最終標記階段,以隊列對象為根重新標記可能漏標的對象 (按快照的存活關係處理)
- 新分配對象區域 [nextTAMS, top) 可能存在浮動垃圾,將在下次被收集
-
最終標記 (Final Marking): 暫停所有的用戶線程,對之前漏標的對象進行一個標記。需要 STW
-
篩選回收( Live Data Counting and Evacuation):更新Region的統計數據,對各個 Region 的回收價值進行一個排序,根據用戶所設置的停頓時間制定一個回收計劃,自由選擇任意個 Region 進行回收。將需要回收的Region 複製到空的 Region 區域中,再清除掉原來的整個Region區域。這塊還涉及到對象的移動所以需要暫停所有的用戶線程,多條回收器線程並行完成。需要 STW
- 為什麼需要 Stop The World呢?因為在篩選回收階段首先會對各個Region的回收價值和成本進行排序,根據用戶所期望的GC停頓時間(可以用JVM參數 -XX:MaxGCPauseMillis 指定)來制定回收計劃,可以自由選擇任意多個Region構成回收集,然後把決定回收的那一部分Region的存活對象複製到空的Region中,再清理掉整箇舊Region的全部空間
- 其實也可以做到與用戶程序一起並發執行,但是停頓用戶線程將大幅提高收集效率
G1 與 CMS 的區別
- G1 從整體來看是基於 「標記—整理」 算法實現的收集器,從局部(兩個 Region 之間)上來看是基於「複製」算法實現的,這意味着 G1 運作期間不會產生內存空間碎片,收集後能提供規整的可用內存
- G1 SATB 利用 write barrier 將所有即將被刪除的引用關係的舊引用記錄下來,最後以這些舊引用為根 Stop The World 地重新掃描一遍即可避免漏標問題。 因此 G1 Final Marking 階段 Stop The World 與 CMS 的 remark 有一個本質上的區別,那就是這個暫停只需要掃描以 write barrier 所追蹤到對象為根的對象, 而 CMS 的 remark 需要重新掃描整個根集合(產生新的根對象指向引用,需要掃描整個根集合),因而 CMS remark 有可能會非常慢
G1 中的三種垃圾回收模式
- YoungGC 觸發條件:young eden 區不夠用
- Mixed GC 觸發條件
- 在 YoungGC 之後,會觸發 Concurrent Marking 並發階段,接着進行 mixed GC,mixed GC 主要工作就是回收並發標記過程中篩選出來的 Region 。和 young GC 流程基本一致
- Full GC 觸發條件
- mixed GC 趕不上內存分配的速度,只能通過 full GC 來釋放內存,這種情況解決方案後面再說
- metaSpace 不足,對於大量使用反射,動態代理的類,由於動態代理的每個類都會生成一個新的類,同時
class
信息會存放在元空間,因此如果元空間不足, G1 會靠 full GC 來擴容元空間,這種情況解決方案就是擴大初始元空間大小 - humongous 分配失敗, G1 分配大對象時,會靠 concurrent marking 或 full GC 回收空間,因此如果大對象分配失敗,則可能會引發 full GC
G1調優參數
- 開啟參數:-XX:+UseG1GC
- 最大GC暫停時間: -XX:MaxGCPauseMillis
- 不要設置年輕代大小:不要使用
-Xmn
,因為 G1 是通過需要擴展或縮小年輕代大小,如果設置了年輕代大小,則會導致 G1 無法使用暫停時間目標
5 Safe Point 和 Safe Region
- jvm 準備進行 GC 階段,並不是隨時都能開始的,需要用戶線程進入一個安全的狀態,才能開始 GC 操作。這個狀態 被稱為 safe point,在代碼上特定的位置點有下面幾種
- 方法返回之前
- 調用某個方法之後
- 拋出異常位置
- 循環的末尾
- 用戶線程執行到安全點時,會輪詢 GC 中斷標誌,一旦出現則在安全點主動掛起線程
- safe point 解決了用戶線程停頓,讓 jvm 進入GC。但如果用戶線程本身就處於 sleep 和 wait 狀態呢,線程不執行,也達到了不了 safe point 位置。Safe Region 可以解決類似問題,Safe Region 是指在一段代碼片段中,引用關係不會發生變化。在這個區域內的任意地方開始 GC 都是安全的
OopMap(Ordinary Object Pointer,普通對象指針)
- 如何確定 GC ROOT 的對象呢,檢查完所有執行上下文和全局的引用位置?實際上 jvm 使用了 OopMap 記錄棧上本地變量到堆上對象的引用關係,避免從全局性引用和執行上下文中逐個查找 GC ROOT,加快枚舉根節點的速度,幫助HotSpot實現準確式GC
- JIT編譯過後的方法也會在一些特定的位置記錄下OopMap。特定的位置如下
- 循環的末尾
- 方法臨返回前 / 調用方法的call指令後
- 可能拋異常的位置
6 什麼是 TLAB 和 PLAB ?
TLAB
堆內存是所有線程共享的,jvm 在並發的環境進行內存分配存在同步競爭,為了加快對象的分配創建,jvm 為每個線程分配了一個私有緩存區域(在Eden空間內),這就是 Thread Local Allocation Buffer。使用TLAB可以避免一系列的非線程安全問題,同時還能夠提升內存分配的吞吐量。如果私有 TLAB 使用完,則使用全局的
PLAB
PLAB 即 Promotion Local Allocation Buffers,用在年輕代對象晉陞到老年代時。在多線程並行執行 YGC 時,可能有很多對象需要晉陞到老年代,為了加快內存分配,於是有了 PLAB
7 CMS、G1 新生代的 GC 如何避免全堆掃描?
常見的 GC 利用了記憶集,記錄分代 GC中 老年代對象指向新年代對象的引用關係,以此避免掃描老年代對象區域
- CMS 使用 CardTable(卡表)的數據結構來標記老年代的某一塊內存區域中的對象是否持有新生代對象的引用。point out 結構
- Card Table: 卡表的數量取決於老年代的大小和每張卡對應的內存大小,每張卡在卡表中對應一個比特位,當老年代中的某個對象持有了新生代對象的引用時,JVM就把這個對象對應的Card所在的位置標記為dirty(bit位設置為1),這樣在Minor GC時就不用掃描整個老年代,而是掃描Card為Dirty對應的那些內存區域
- G1 為了避免 young GC 時,掃描整個老年代,G1 引入了 Card Table 和 Remember Set 的概念
- RSet:全稱 Remembered Sets, 用來記錄外部指向本 Region 的所有引用,每個 Region 維護一個 RSet。point in 結構,雙向指向
- 下圖展示的是 RSet 與 Card Table 的關係。每個 Region 被分成了多個 Card Table,其中綠色部分的 Card 表示該 Card 中有對象引用了其他 Card 中的對象,這種引用關係用藍色實線表示。RSet 其實是一個 HashTable,Key 是 Region 的起始地址,Value 是 Card Table (位元組數組),位元組數組下標表示 Card 的空間地址,當該地址空間被引用的時候會被標記為 dirty_card
為什麼 G1 不維護年輕代到老年代的記憶集?
- G1 分 young GC 和 mixed GC,full GC。young gc 會選所有年輕代的區域進行回收;midex gc 會選所有年輕代的區域和一些收集收益高的老年代區域進行回收,而full GC 則是全堆回收。三種 GC,年輕代的區域都在回收範圍內,所以不需要額外記錄年輕代到老年代的跨代引用
8 CMS、G1 為了防止並發時的漏標分別用了什麼手段?
三色標誌法
- 黑色:從GCRoots開始,已掃描過它全部引用的對象,標記為黑色
- 灰色:掃描過對象本身,還沒完全掃描過它全部引用的對象,標記為灰色
- 白色:還沒掃描過的對象,標記為白色
- 並發執行漏標的兩個充分必要條件
- 賦值器插入了一條或多條從黑色對象到白色對象的新引用
- 賦值器刪除了全部從灰色對象到該白色對象的直接或間接引用
漏標 CMS 解決方案-增量更新(Incremental Update)
增量更新要破壞的是第一個條件,當黑色對象插入新的指向白色對象的引用時,用寫屏障將新插入的引用記錄下來,等並發掃描結束之後,再以這些記錄過的黑色對象為根,重新掃描一次
漏標 G1 解決方案-原始快照(Snapshot At TheBeginning,SATB)
SATB 要破壞的是第二個條件,當灰色對象要刪除指向白色對象的引用時,用寫屏障將這個要刪除的引用記錄下來,在並發掃描結束之後,再將這些記錄過的引用關係中的灰色對象為根,重新掃描一次
9 什麼是 logging write barrier
- write barrier 的操作邏輯是複雜的,是為了減少對應用 mutator 線程性能的影響,G1將一部分原本要在 write barrier 里做的邏輯分離出來交給異步線程並發執行:mutator 線程在寫屏障里把分離的邏輯信息以 log 形式放到一個隊列里,然異步線程再從隊列里取出 log 批量執行
- 以SATB write barrier為例,每個Java線程有一個獨立的、定長的 SATBMarkQueue,mutator在 barrier 里把old_value壓入該隊列中。一個隊列滿了之後,它就會被加到全局的 SATB 隊列集合 SATBMarkQueueSet 里等待處理。後台異步線程會掃描,如果超過一定閾值就會處理,開始處理
10 CMS 常見問題
最終標記階段停頓時間過長問題
- CMS的GC停頓時間約80%都在最終標記階段(Final Remark),若該階段停頓時間過長,常見原因是新生代對老年代的無效引用,在 並發可取消預清理 階段中,執行閾值時間內未完成循環,來不及觸發 young GC,清理這些無效引用
- 通過添加參數:-XX:+CMSScavengeBeforeRemark。在執行 Final Remark 操作之前先觸發 young GC,從而減少新生代對老年代的無效引用,降低最終標記階段的停頓
Promotion Failure
- 該問題是在進行 young gc 時,Survivor Space放不下,對象只能放入老年代,而此時老年代也放不下,則會產生 Promotion Failure
concurrent mode failure
CMS 垃圾收集器特有的錯誤,CMS 的垃圾清理和引用線程是並行進行的,如果在並行清理的過程中老年代的空間不足以容納應用產生的垃圾(也就是老年代正在清理,從年輕代晉陞了新的對象,或者直接分配大對象年輕代放不下導致直接在老年代生成,這時候老年代也放不下),則會拋出 concurrent mode failure
- 垃圾產生速度超過清理速度
- 晉陞閾值過小,設置
-XX:MaxTenuringThreshold=n
- 降低觸發CMS GC的閾值,開啟根據閾值觸發CMS GC開關:
-XX:+UseCMSInitiatingOccupancyOnly
,和參數-XX:CMSInitiatingOccupancyFraction=n
的值(默認為 92%),讓CMS GC儘早執行,以保證有足夠的空間 - 增加CMS線程數,即參數-XX:ConcGCThreads
- Survivor 空間過小,加大;Eden 區過小,加大。整體內存下導致晉陞速率提高,老年區空間不足
- 存在大對象分配
- 晉陞閾值過小,設置
- CMS GC 發生 concurrent mode failure 時的 full GC 為什麼是單線程的?
- CMS GC 不兼容並發回收 young 區
內存碎片問題
- 開啟空間碎片整理,並將空間碎片整理周期設置在合理範圍。開啟空間碎片整理
-XX:+UseCMSCompactAtFullCollection
,讓CMS在進行一定次數 Full GC 進行碎片壓縮-XX:CMSFullGCsBeforeCompaction=n
11 GC 事件和日誌分析
GC 指標
- 延遲、GC 暫停時間(stop the world)
- 吞吐量(應用服務在非 GC 功能上運行的耗時百分比)
- GC 頻率
- CPU 耗時
GC事件分類
- Young GC, 新生代內存的垃圾收集事件稱為Young GC(又稱Minor GC),當JVM無法為新對象分配在新生代內存空間時總會觸發 Young GC
- Old GC,只清理老年代空間的GC事件,只有CMS的並發收集是這個模式
- Mixed GC,清理整個新生代以及部分老年代的GC,只有G1有這個模式
- Full GC,清理整個堆的GC事件,包括新生代、老年代、元空間等
GC日誌分析
- 開啟 GC 日誌分析
-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps
需要對 GC 進行完整的監控,監控各年代佔用大小、YGC 觸發頻率、Full GC 觸發頻率,對象分配速率等等
GCLocker Initiated GC
- 如果線程執行在 JNI 臨界區時,剛好需要進行 GC,此時 GC Locker 將會阻止 GC 的發生,同時阻止其他線程進入 JNI 臨界區,直到最後一個線程退出臨界區時觸發一次 GC
動態擴容引起的空間震蕩
- 服務剛剛啟動時 GC 次數較多,最大空間剩餘很多但是依然發生 GC。在 JVM 的參數中
-Xms
和-Xmx
設置的不一致,在初始化時只會初始-Xms
大小的空間存儲信息,每當空間不夠用時再向操作系統申請,這樣的話必然要進行一次 GC - 盡量將成對出現的空間大小配置參數設置成固定的,如
-Xms
和-Xmx
,-XX:MaxNewSize
和-XX:NewSize
,-XX:MetaSpaceSize
和-XX:MaxMetaSpaceSize
等
12 JVM 常用參數匯總
- 通用配置參數
參數 | 說明 | 實例 |
---|---|---|
-Xms | 初始堆大小,默認物理內存的1/64 | -Xms512M |
-Xmx | 最大堆大小,默認物理內存的1/4 | -Xms2G |
-Xmn | 新生代內存大小,官方推薦為整個堆的3/8 | -Xmn512M |
-XX:NewRatio=n | 設置新生代和年老代的比值。如: 3,表示年輕代與年老代比值為1:3,年輕代占整個年輕代年老代和的1/4 | -XX:NewRatio=3 |
-XX:SurvivorRatio=n | 年輕代中Eden區與兩個Survivor區的比值。注意Survivor區有兩個。如: 8,表示Eden:Survivor=8:1:1,一個Survivor區占整個年輕代的1/10 | -XX:SurvivorRatio=8 |
-Xss | 線程堆棧大小,jdk1.5及之後默認1M,之前默認256k | -Xss512k |
-XX:PermSize=n | 永久代初始值,默認為物理內存的1/64 | -XX:PermSize=128M |
-XX:MaxPermSize=n | 永久代最大值,默認為物理內存的1/4 | -XX:MaxPermSize=256M |
-verbose:class | 在控制台打印類加載信息 | |
-verbose:gc | 在控制台打印垃圾回收日誌 | |
-XX:+PrintGC | 打印GC日誌,內容簡單 | |
-XX:+PrintGCDetails | 打印GC日誌,內容詳細 | |
-XX:+PrintGCDateStamps | 在GC日誌中添加時間戳 | |
-Xloggc:filename | 指定gc日誌路徑 | -Xloggc:/data/jvm/gc.log |
-XX:+DisableExplicitGC | 關閉System.gc() | |
-XX:+UseBiasedLocking | 自旋鎖機制的性能改善 | |
-XX:PretenureSizeThreshold | 對象超過多大是直接在舊生代分配,默認值 0 ,單位位元組 | |
-XX:TLABWasteTargetPercent | TLAB 佔eden區的百分比 默認值 1% | |
-XX:+CollectGen0First | fullGC 時是否先 youngGC 默認值 false | |
-XX:+PrintHeapAtGC | 打印 GC 前後的詳細堆棧信息 | |
-XX:ParallelGCThreads=n | 設置並行收集器時使用的CPU數。此值最好配置與處理器數目相等,同樣適用於CMS | -XX:ParallelGCThreads=4 |
- 年輕代
參數 | 說明 |
---|---|
-XX:+UseSerialGC | 年輕代設置串行收集器Serial |
-XX:+UseParallelGC | 年輕代設置並行收集器Parallel Scavenge |
-XX:UseParNewGC | 啟用ParNew收集器 |
-XX:MaxTenuringThreshold | 幾次 youngGC 後會被分到老年代,默認是15次 |
-XX:MaxGCPauseMillis=n | 年輕代垃圾回收的最長時間,如果無法滿足此時間,JVM會自動調整年輕代大小,以滿足此值 |
- 老年代
參數 | 說明 |
---|---|
-XX:+UseParallelOldGC | 設置老年代為並行收集器ParallelOld收集器 |
-XX:+UseConcMarkSweepGC | 設置老年代並發收集器CMS,且默認使用parNew作為新生代的垃圾回收 |
-XX+UseCMSCompactAtFullCollection | fullGC過後,開啟對老年代的內存壓縮,我們知道CMS使用的標記清除算法,會產生內存碎片,所以需要內存壓縮 |
-XX:CMSFullGCsBeforeCompaction=n | 經過幾次FullGC後進行內存壓縮,默認是 0 |
-XX:ParallelCMSThreads=n | CMS 過程並發線程數 |
-XX:+CMSParallelInitialMarkEnabled | 為了減少 CMS 初始標誌暫停的時間,開啟並行標誌 |
-XX:+CMSParallelRemarkEnabled | 為了減少 CMS 第二次暫停的時間,開啟並行remark |
-XX:+CMSScavengeBeforeRemark | 如果 CMS remark 暫停時間過長的話,可以開啟該選項,強制remark之前開始一次minor gc,減少remark的暫停時間,但是在remark之後也將立即開始又一次minor gc |
- G1 特有參數
參數 | 說明 |
---|---|
-XX:+UseG1GC | 使用 G1 (Garbage First) 垃圾收集器 |
-XX:InitiatingHeapOccupancyPercent | 老年代佔用空間達到整堆內存閾值(默認45%),則執行新生代和老年代的混合收集(MixedGC),比如我們之前說的堆默認有2048個region,如果有接近1000個region都是老年代的region,則可能 就要觸發MixedGC了 |
-XX:MaxGCPauseMillis | 目標暫停時間(默認200ms) 也就是垃圾回收的時候允許停頓的時間 |
-XX:G1MixedGCCountTarget | 在一次回收過程中指定做幾次篩選回收(默認8次),在最後一個篩選回收階段可以回收一 會,然後暫停回收,恢復系統運行,一會再開始回收,這樣可以讓系統不至於單次停頓時間過長 |
-XX:G1HeapWastePercent | (默認5%) 在混合回收時,一旦空閑出來的Region數量達到了堆內存的5%,此時就會立 即停止混合回收,意味着本次混合回收就結束了 |
-XX:ConcGCThreads=n | 並發垃圾收集器使用的線程數量. 默認值隨JVM運行的平台不同而不同 |
歡迎指正文中錯誤
參考文章
- JVM(四)分代垃圾回收機制和垃圾回收算法
- JVM(五) GC 底層細節
- ZGC設計與實現
- 炸了!一口氣問了我18個JVM問題
- concurrent mode failure
- 老大難的Java GC原理和調優,看這篇就夠了
- Java中9種常見的CMS GC問題分析與解決
- 從實際案例聊聊Java應用的GC優化
- JDK 11 ZGC簡介
- JVM – 解讀GC中的 Safe Point & Safe Region