基礎篇:java GC 總結,建議收藏

2022-05-24 12:00:59
  • 垃圾標記演演算法
  • 垃圾回收演演算法
  • major gc、minor 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、minor gc、mixed gc又是什麼,怎麼觸發的?

  • major gc:個人理解應該是指 old gc。不過有些人認為和 full gc 等價
    • 執行 System.gc()、jmap -dump 等命令會觸發 full gc
    • 有永久代的話,永久代滿了也會觸發 full gc
    • 大物件直接在老年代申請分配,如果此時老年代空間不足則會觸發 full gc
    • 新生代物件 gc 年齡到達閾值需要晉升,老年代如果放不下的話會觸發 full gc
  • minor 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 是隱式存活的
  • 並行標記(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執行的平臺不同而不同

歡迎指正文中錯誤

參考文章