Java GC 總結(jié),建議收藏
垃圾標記算法 垃圾回收算法 major gc、minor gc、full gc、mixed gc 又是什么,怎么觸發(fā)的 垃圾回收器的介紹 Safe Point 和 Safe Region 什么是 TLAB 和 PLAB ? CMS、G1 新生代的 GC 如何避免全堆掃描 CMS 和 G1 為了防止并發(fā)時的漏標分別用了什么手段 什么是 logging write barrier CMS 常見問題 GC 事件和日志分析 JVM 常用參數(shù)匯總 關(guān)注公眾號,一起交流:潛行前行
1 垃圾標記算法
引用計算法
引用計數(shù)法是最簡單有效的垃圾標記方法,它會把對象被引用的次數(shù)記錄下來,當(dāng)被引用時,計數(shù)加一。當(dāng)其他變量不再指向目標對象時,則引用減一。對象引用數(shù)為零時 ,則可以進行內(nèi)存回收釋放 無法解決循環(huán)引用問題
根可達性分析
從 GC Root 開始進行對象搜索,可以被搜索到的對象即為可達對象,不可達對象便可以作為垃圾被回收掉。目前 Java 中主流的虛擬機均采用此算法 在Java語言里,可作為GC Roots的對象包括下面幾種: 虛擬機棧(棧幀中的本地變量表)中的引用的對象 方法區(qū)中的類靜態(tài)屬性引用的對象 方法區(qū)中的常量引用的對象 本地方法棧中JNI(即一般說的Native方法)的引用的對象
2 垃圾回收算法
復(fù)制:將一塊內(nèi)存區(qū)域進行對半分,當(dāng)一半的內(nèi)存使用完時,便將其中存活的對象復(fù)制到另一半內(nèi)存區(qū)域中,原先的區(qū)域進行回收。不存在內(nèi)存碎片問題,實現(xiàn)簡單運行高效,但是有個缺點,就是對內(nèi)存的利用率只有 50% 標記清除:算法分為 “標記” 和 “清理”兩個階段 標記階段:標記出所有需要回收的對象 清除階段:標記完成后,統(tǒng)一清除回收被標記的對象 由于對象之前在內(nèi)存中的分布是無規(guī)律的,標記清除算法會產(chǎn)生大量不連續(xù)的內(nèi)存碎片,造成連續(xù)的大內(nèi)存空間缺失,阻礙大內(nèi)存對象的分配,嚴重時會觸發(fā)垃圾回收,甚至出現(xiàn) OutOfMemeryError。而且如果大部分的對象是朝生夕死的,標記的對象就會更多,效率更低 標記整理:步驟與 標記清除步 驟一致,但它的第二步是整理標記之外的所有對象,將所有對象向前移動,之后直接回收掉存活對象之外的內(nèi)存區(qū)域。標記整理 不會存在內(nèi)存碎片,但是效率是偏低的
按年代劃分-分代算法
新生代回收(Minor GC/Young GC):指只是進行新生代的回收 老年代回收(Major GC/Old GC):指只是進行老年代的回收。目前只有 CMS 垃圾回收器會有這個單獨的回收老年代的行為 整堆回收(Full GC):收集整個堆,包括年輕代、老年代,如果有永久代的話還包括永久代
3 major gc、minor gc、mixed gc又是什么,怎么觸發(fā)的?
major gc:個人理解應(yīng)該是指 old gc。不過有些人認為和 full gc 等價 執(zhí)行 System.gc()、jmap -dump 等命令會觸發(fā) full gc 有永久代的話,永久代滿了也會觸發(fā) full gc 大對象直接在老年代申請分配,如果此時老年代空間不足則會觸發(fā) full gc 新生代對象 gc 年齡到達閾值需要晉升,老年代如果放不下的話會觸發(fā) full gc minor gc:指的也是年輕代的 young gc 年輕代的 eden 快要被占滿的時候會觸發(fā) young gc eden 快滿的觸發(fā)因素有兩個,一個是為對象分配內(nèi)存不夠,一個是為 TLAB 分配內(nèi)存不夠 mixed gc:這個是 G1 收集器特有的,指的是收集整個年輕代和部分老年代的 GC 在 young gc 之后,當(dāng)老年代的堆占有率達到參數(shù) (-XX:InitiatingHeapOccupancyPercent) 設(shè)定的值時則觸發(fā) mixed GC
4 垃圾回收器的介紹
Serial New 和 Serial Old
jvm 誕生初期所采用的垃圾回收器,單線程,獨占式,適合單 CPU 單線程進行垃圾回收時,必須暫停所有的工作線程,直到它回收結(jié)束。這個暫停稱之為 Stop The World,但是 STW 會帶來差的性能影響
Parallel Scavenge ?和 Parallel Old
為了提高 jvm 的回收效率,jvm 使用了多線程的垃圾回收器,關(guān)注吞吐量的垃圾回收器,可以更高效的利用CPU 時間,從而盡快完成程序的運算任務(wù) Parallel Scavenge 收集器提供了兩個參數(shù)用于精確控制吞吐量,可以分別是控制最大垃圾收集停頓時間 -XX:MaxGCPauseMillis參數(shù)以及直接設(shè)置吞吐量大小的 -XX:GCTimeRatio參數(shù)。其也經(jīng)常被稱為“吞吐量優(yōu)先”收集器
ParNew 和 CMS
ParNew 與 Parallel Scavenge 差不多。區(qū)別是 Parallel Scavenge 是一個可控制的吞吐量并行垃圾回收器,ParNew 沒有參數(shù)來控制吞吐量和停頓時間 ParNew 可以和 CMS 搭配使用,而 Parallel Scavenge 不可用,其根本原因是設(shè)計上就是沒想過兼容 CMS CMS(Concurrent Mark Sweep) 是一款針對老年代的垃圾回收器,追求最短的回收停頓時間(STW)為目標,采用的是 標記-清除算法
CMS 的回收流程
初始標記:只標記與 GC Root 有直接關(guān)聯(lián)的對象,這類的對象比較少,標記快。需要 STW 并發(fā)標記:并發(fā)標記與初始化標記的對象有關(guān)聯(lián)的所有對象,這類的對象比較多所以采用的并發(fā),與用戶線程一起跑 **并發(fā)預(yù)清理(Concurrent Preclean)**:并發(fā)標記階段是與應(yīng)用線程并發(fā)執(zhí)行的,有些引用關(guān)系已經(jīng)發(fā)生改變,通過卡片標記(Card Marking),如果引用關(guān)系發(fā)生改變,JVM會將發(fā)生改變的區(qū)域標記位“臟區(qū)”(Dirty Card),然后在本階段,這些臟區(qū)會被找出來,刷新引用關(guān)系,清除“臟區(qū)”標記 **并發(fā)可取消的預(yù)清理(Concurrent Abortable Preclean)**:和并發(fā)預(yù)清理階段工作差不多,用來減少Final Remark 階段的暫停時間。該階段會不斷循環(huán)處理:標記老年代的可達對象、掃描處理Dirty Card區(qū)域中的對象引用關(guān)系。循環(huán)中斷條件 達到循環(huán)次數(shù) 達到循環(huán)執(zhí)行時間閾值 新生代內(nèi)存使用率達到閾值 最終標記 (Final Remark):修正并發(fā)標記時候標記產(chǎn)生異動的對象標記,這塊的時間比初始標記稍長一些,但是比起并發(fā)標記要快很多。需要 STW 遍歷新生代對象,重新標記 根據(jù)GC Roots,重新標記 遍歷老年代的Dirty Card,重新標記 并發(fā)清除(Concurrent Sweep):與用戶線程一起運行,進行對象回收清除 缺點 浮動垃圾:在CMS進行并發(fā)清除階段,GC線程是并發(fā)的,所以在清除的時候用戶線程會產(chǎn)出新的垃圾。因此在進行回收時需要預(yù)留一部分的空間來存放這些新產(chǎn)生垃圾(JDK 1.6 設(shè)置的閾值為92%)。但是如果用戶線程產(chǎn)出的垃圾比較快,預(yù)留內(nèi)存放不下的時候就會出現(xiàn) Concurrent Mode Failure,這時虛擬機將臨時啟用 Serial Old 來替代 CMS。 內(nèi)存碎片:因為采用的是 標記-清除 算法,會產(chǎn)生內(nèi)存碎片
G1
G1 垃圾回收器的設(shè)計思想與上面的垃圾回收器的都不一樣,前面垃圾回收器采用的都是 分代劃分 的方式進行設(shè)計的,而 G1 則是將堆看作是一個整體的區(qū)域,這個區(qū)域被劃分成了一個個大小一致的獨立區(qū)域(Region),而每個區(qū)域都可以根據(jù)需要成為 Eden、Survivor 以及老年代區(qū)域
G1的回收流程

初始標記(Initial Marking):標記與 GC Roots 能關(guān)聯(lián)到的對象,修改 TAMS (Top at Mark Start) 給堆對象拍個快照,這個過程是需要暫停用戶線程的,但是耗時非常的短。需要 STW

image.png Region 記錄著兩個 top-at-mark-start (TAMS) 指針,分別為? prevTAMS?和?nextTAMS。在 nextTAMS~top的之間對象是新分配的,被視為隱式 marked(存活對象)。對象是否存活使用bitmap位圖標志,prevBitmap記錄第 n-1 輪 concurrent marking 所得的對象存活狀態(tài),nextBitmap記錄第 n 輪 concurrent marking 的結(jié)果top 是該 Region 的當(dāng)前分配指針,[bottom, top) 是當(dāng)前該 Region 已用的部分,[top, end) 是尚未使用的可分配空間 [bottom, prevTAMS):這部分里的對象存活信息可以通過? prevBitmap?來得知[prevTAMS, nextTAMS):這部分里的對象在第 n-1 輪 concurrent marking 是隱式存活的 [nextTAMS, top):這部分里的對象在第 n 輪 concurrent marking 是隱式存活的 并發(fā)標記(Concurrent Marking):進行掃描標記所有可回收的對象。當(dāng)掃描完成后,并發(fā)會有引用變化的對象,而這些對象會漏標這些漏標的對象會被 SATB 算法所解決
SATB(snapshot-at-the-beginning):舊對象區(qū)域 [bottom, nextTAMS) ?按 nextTAMS 生成時的存活快照為準,即對象在 nextTAMS 生成之后變成垃圾也不會被回收 如果在并發(fā)標記時,引用發(fā)生改變的對象將被放入 satb_mark_queue 隊列(寫屏障實現(xiàn)),之后在最終標記階段,以隊列對象為根重新標記可能漏標的對象 (按快照的存活關(guān)系處理) 新分配對象區(qū)域 [nextTAMS, top) 可能存在浮動垃圾,將在下次被收集 最終標記 (Final Marking):暫停所有的用戶線程,對之前漏標的對象進行一個標記。需要 STW
篩選回收( Live Data Counting and Evacuation):更新Region的統(tǒng)計數(shù)據(jù),對各個 Region 的回收價值進行一個排序,根據(jù)用戶所設(shè)置的停頓時間制定一個回收計劃,自由選擇任意個 Region 進行回收。將需要回收的Region 復(fù)制到空的 Region 區(qū)域中,再清除掉原來的整個Region區(qū)域。這塊還涉及到對象的移動所以需要暫停所有的用戶線程,多條回收器線程并行完成。需要 STW
為什么需要 Stop The World呢?因為在篩選回收階段首先會對各個Region的回收價值和成本進行排序,根據(jù)用戶所期望的GC停頓時間(可以用JVM參數(shù) -XX:MaxGCPauseMillis 指定)來制定回收計劃,可以自由選擇任意多個Region構(gòu)成回收集,然后把決定回收的那一部分Region的存活對象復(fù)制到空的Region中,再清理掉整個舊Region的全部空間 其實也可以做到與用戶程序一起并發(fā)執(zhí)行,但是停頓用戶線程將大幅提高收集效率
G1 與 CMS 的區(qū)別
G1 從整體來看是基于 “標記—整理” 算法實現(xiàn)的收集器,從局部(兩個 Region 之間)上來看是基于“復(fù)制”算法實現(xiàn)的,這意味著 G1 運作期間不會產(chǎn)生內(nèi)存空間碎片,收集后能提供規(guī)整的可用內(nèi)存 G1 SATB 利用 write barrier 將所有即將被刪除的引用關(guān)系的舊引用記錄下來,最后以這些舊引用為根 Stop The World 地重新掃描一遍即可避免漏標問題。因此 G1 Final Marking 階段 Stop The World 與 CMS 的 remark 有一個本質(zhì)上的區(qū)別,那就是這個暫停只需要掃描以 write barrier 所追蹤到對象為根的對象, 而 CMS 的 remark 需要重新掃描整個根集合(產(chǎn)生新的根對象指向引用,需要掃描整個根集合),因而 CMS remark 有可能會非常慢
G1 中的三種垃圾回收模式
YoungGC 觸發(fā)條件:young eden 區(qū)不夠用 Mixed GC 觸發(fā)條件 在 YoungGC 之后,會觸發(fā) Concurrent Marking 并發(fā)階段,接著進行 mixed GC,mixed GC 主要工作就是回收并發(fā)標記過程中篩選出來的 Region 。和 ?young GC 流程基本一致 Full GC 觸發(fā)條件 mixed GC 趕不上內(nèi)存分配的速度,只能通過 full GC 來釋放內(nèi)存,這種情況解決方案后面再說 metaSpace 不足,對于大量使用反射,動態(tài)代理的類,由于動態(tài)代理的每個類都會生成一個新的類,同時 class信息會存放在元空間,因此如果元空間不足, G1 會靠 full GC 來擴容元空間,這種情況解決方案就是擴大初始元空間大小humongous 分配失敗, G1 分配大對象時,會靠 concurrent marking 或 full GC 回收空間,因此如果大對象分配失敗,則可能會引發(fā) full GC
G1調(diào)優(yōu)參數(shù)
開啟參數(shù):-XX:+UseG1GC 最大GC暫停時間: -XX:MaxGCPauseMillis 不要設(shè)置年輕代大小:不要使用 -Xmn,因為 G1 是通過需要擴展或縮小年輕代大小,如果設(shè)置了年輕代大小,則會導(dǎo)致 G1 無法使用暫停時間目標
5 Safe Point 和 Safe Region
jvm 準備進行 GC 階段,并不是隨時都能開始的,需要用戶線程進入一個安全的狀態(tài),才能開始 GC 操作。這個狀態(tài) 被稱為 safe point,在代碼上特定的位置點有下面幾種 方法返回之前 調(diào)用某個方法之后 拋出異常位置 循環(huán)的末尾 用戶線程執(zhí)行到安全點時,會輪詢 GC 中斷標志,一旦出現(xiàn)則在安全點主動掛起線程 safe point 解決了用戶線程停頓,讓 jvm 進入GC。但如果用戶線程本身就處于 sleep 和 wait 狀態(tài)呢,線程不執(zhí)行,也達到了不了 safe point 位置。Safe Region 可以解決類似問題,Safe Region 是指在一段代碼片段中,引用關(guān)系不會發(fā)生變化。在這個區(qū)域內(nèi)的任意地方開始 GC 都是安全的
OopMap(Ordinary Object Pointer,普通對象指針)
如何確定 GC ROOT 的對象呢,檢查完所有執(zhí)行上下文和全局的引用位置?實際上 jvm 使用了 OopMap 記錄棧上本地變量到堆上對象的引用關(guān)系,避免從全局性引用和執(zhí)行上下文中逐個查找 GC ROOT,加快枚舉根節(jié)點的速度,幫助HotSpot實現(xiàn)準確式GC JIT編譯過后的方法也會在一些特定的位置記錄下OopMap。特定的位置如下 循環(huán)的末尾 方法臨返回前 / 調(diào)用方法的call指令后 可能拋異常的位置
6 什么是 TLAB 和 PLAB ?
TLAB
堆內(nèi)存是所有線程共享的,jvm 在并發(fā)的環(huán)境進行內(nèi)存分配存在同步競爭,為了加快對象的分配創(chuàng)建,jvm 為每個線程分配了一個私有緩存區(qū)域(在Eden空間內(nèi)),這就是 Thread Local Allocation Buffer。使用TLAB可以避免一系列的非線程安全問題,同時還能夠提升內(nèi)存分配的吞吐量。如果私有 TLAB 使用完,則使用全局的
PLAB
PLAB 即 Promotion Local Allocation Buffers,用在年輕代對象晉升到老年代時。在多線程并行執(zhí)行 YGC 時,可能有很多對象需要晉升到老年代,為了加快內(nèi)存分配,于是有了 PLAB
7 CMS、G1 新生代的 GC 如何避免全堆掃描?
常見的 GC 利用了記憶集,記錄分代 GC中 老年代對象指向新年代對象的引用關(guān)系,以此避免掃描老年代對象區(qū)域
CMS 使用 CardTable(卡表)的數(shù)據(jù)結(jié)構(gòu)來標記老年代的某一塊內(nèi)存區(qū)域中的對象是否持有新生代對象的引用。point out 結(jié)構(gòu) Card Table: 卡表的數(shù)量取決于老年代的大小和每張卡對應(yīng)的內(nèi)存大小,每張卡在卡表中對應(yīng)一個比特位,當(dāng)老年代中的某個對象持有了新生代對象的引用時,JVM就把這個對象對應(yīng)的Card所在的位置標記為dirty(bit位設(shè)置為1),這樣在Minor GC時就不用掃描整個老年代,而是掃描Card為Dirty對應(yīng)的那些內(nèi)存區(qū)域 G1 為了避免 young GC 時,掃描整個老年代,G1 引入了 Card Table 和 Remember Set 的概念 RSet:全稱 Remembered Sets, 用來記錄外部指向本 Region 的所有引用,每個 Region 維護一個 RSet。point in 結(jié)構(gòu),雙向指向 下圖展示的是 RSet 與 Card Table 的關(guān)系。每個 Region 被分成了多個 Card Table,其中綠色部分的 Card 表示該 Card 中有對象引用了其他 Card 中的對象,這種引用關(guān)系用藍色實線表示。RSet 其實是一個 HashTable,Key 是 Region 的起始地址,Value 是 Card Table (字節(jié)數(shù)組),字節(jié)數(shù)組下標表示 Card 的空間地址,當(dāng)該地址空間被引用的時候會被標記為 dirty_card

為什么 G1 不維護年輕代到老年代的記憶集?
G1 分 young GC 和 mixed GC,full GC。young gc 會選所有年輕代的區(qū)域進行回收;midex gc 會選所有年輕代的區(qū)域和一些收集收益高的老年代區(qū)域進行回收,而full GC 則是全堆回收。三種 GC,年輕代的區(qū)域都在回收范圍內(nèi),所以不需要額外記錄年輕代到老年代的跨代引用
8 CMS、G1 為了防止并發(fā)時的漏標分別用了什么手段?
三色標志法
黑色:從GCRoots開始,已掃描過它全部引用的對象,標記為黑色 灰色:掃描過對象本身,還沒完全掃描過它全部引用的對象,標記為灰色 白色:還沒掃描過的對象,標記為白色 
并發(fā)執(zhí)行漏標的兩個充分必要條件 賦值器插入了一條或多條從黑色對象到白色對象的新引用 賦值器刪除了全部從灰色對象到該白色對象的直接或間接引用
漏標 CMS 解決方案-增量更新(Incremental Update)
增量更新要破壞的是第一個條件,當(dāng)黑色對象插入新的指向白色對象的引用時,用寫屏障將新插入的引用記錄下來,等并發(fā)掃描結(jié)束之后,再以這些記錄過的黑色對象為根,重新掃描一次
漏標 G1 解決方案-原始快照(Snapshot At TheBeginning,SATB)
SATB 要破壞的是第二個條件,當(dāng)灰色對象要刪除指向白色對象的引用時,用寫屏障將這個要刪除的引用記錄下來,在并發(fā)掃描結(jié)束之后,再將這些記錄過的引用關(guān)系中的灰色對象為根,重新掃描一次
9 什么是 logging write barrier
write barrier 的操作邏輯是復(fù)雜的,是為了減少對應(yīng)用 mutator 線程性能的影響,G1將一部分原本要在 write barrier 里做的邏輯分離出來交給異步線程并發(fā)執(zhí)行:mutator 線程在寫屏障里把分離的邏輯信息以 log 形式放到一個隊列里,然異步線程再從隊列里取出 log 批量執(zhí)行 以SATB write barrier為例,每個Java線程有一個獨立的、定長的 SATBMarkQueue,mutator在 barrier 里把old_value壓入該隊列中。一個隊列滿了之后,它就會被加到全局的 SATB 隊列集合 SATBMarkQueueSet 里等待處理。后臺異步線程會掃描,如果超過一定閾值就會處理,開始處理
10 CMS 常見問題
最終標記階段停頓時間過長問題
CMS的GC停頓時間約80%都在最終標記階段(Final Remark),若該階段停頓時間過長,常見原因是新生代對老年代的無效引用,在 并發(fā)可取消預(yù)清理 階段中,執(zhí)行閾值時間內(nèi)未完成循環(huán),來不及觸發(fā) young GC,清理這些無效引用 通過添加參數(shù):-XX:+CMSScavengeBeforeRemark。在執(zhí)行 Final Remark 操作之前先觸發(fā) young GC,從而減少新生代對老年代的無效引用,降低最終標記階段的停頓
Promotion Failure
該問題是在進行 young gc 時,Survivor Space放不下,對象只能放入老年代,而此時老年代也放不下,則會產(chǎn)生 Promotion Failure
concurrent mode failure
CMS 垃圾收集器特有的錯誤,CMS 的垃圾清理和引用線程是并行進行的,如果在并行清理的過程中老年代的空間不足以容納應(yīng)用產(chǎn)生的垃圾(也就是老年代正在清理,從年輕代晉升了新的對象,或者直接分配大對象年輕代放不下導(dǎo)致直接在老年代生成,這時候老年代也放不下),則會拋出 concurrent mode failure
垃圾產(chǎn)生速度超過清理速度 晉升閾值過小,設(shè)置 -XX:MaxTenuringThreshold=n降低觸發(fā)CMS GC的閾值,開啟根據(jù)閾值觸發(fā)CMS GC開關(guān): -XX:+UseCMSInitiatingOccupancyOnly,和參數(shù)-XX:CMSInitiatingOccupancyFraction=n的值(默認為 92%),讓CMS GC盡早執(zhí)行,以保證有足夠的空間增加CMS線程數(shù),即參數(shù)-XX:ConcGCThreads Survivor 空間過小,加大;Eden 區(qū)過小,加大。整體內(nèi)存下導(dǎo)致晉升速率提高,老年區(qū)空間不足 存在大對象分配 CMS GC 發(fā)生 concurrent mode failure 時的 full GC 為什么是單線程的? CMS GC 不兼容并發(fā)回收 young 區(qū)
內(nèi)存碎片問題
開啟空間碎片整理,并將空間碎片整理周期設(shè)置在合理范圍。開啟空間碎片整理 -XX:+UseCMSCompactAtFullCollection,讓CMS在進行一定次數(shù) Full GC 進行碎片壓縮-XX:CMSFullGCsBeforeCompaction=n
11 GC 事件和日志分析
GC 指標
延遲、GC 暫停時間(stop the world) 吞吐量(應(yīng)用服務(wù)在非 GC 功能上運行的耗時百分比) GC 頻率 CPU 耗時
GC事件分類
Young GC, 新生代內(nèi)存的垃圾收集事件稱為Young GC(又稱Minor GC),當(dāng)JVM無法為新對象分配在新生代內(nèi)存空間時總會觸發(fā) Young GC Old GC,只清理老年代空間的GC事件,只有CMS的并發(fā)收集是這個模式 Mixed GC,清理整個新生代以及部分老年代的GC,只有G1有這個模式 Full GC,清理整個堆的GC事件,包括新生代、老年代、元空間等
GC日志分析
開啟 GC 日志分析 -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps

需要對 GC 進行完整的監(jiān)控,監(jiān)控各年代占用大小、YGC 觸發(fā)頻率、Full GC 觸發(fā)頻率,對象分配速率等等
GCLocker Initiated GC
如果線程執(zhí)行在 JNI 臨界區(qū)時,剛好需要進行 GC,此時 GC Locker 將會阻止 GC 的發(fā)生,同時阻止其他線程進入 JNI 臨界區(qū),直到最后一個線程退出臨界區(qū)時觸發(fā)一次 GC
動態(tài)擴容引起的空間震蕩
服務(wù)剛剛啟動時 GC 次數(shù)較多,最大空間剩余很多但是依然發(fā)生 GC。在 JVM 的參數(shù)中? -Xms?和?-Xmx?設(shè)置的不一致,在初始化時只會初始?-Xms?大小的空間存儲信息,每當(dāng)空間不夠用時再向操作系統(tǒng)申請,這樣的話必然要進行一次 GC盡量將成對出現(xiàn)的空間大小配置參數(shù)設(shè)置成固定的,如? -Xms?和?-Xmx,-XX:MaxNewSize?和?-XX:NewSize,-XX:MetaSpaceSize?和?-XX:MaxMetaSpaceSize?等
12 JVM 常用參數(shù)匯總
通用配置參數(shù)
| 參數(shù) | 說明 | 實例 |
|---|---|---|
| -Xms | 初始堆大小,默認物理內(nèi)存的1/64 | -Xms512M |
| -Xmx | 最大堆大小,默認物理內(nèi)存的1/4 | -Xms2G |
| -Xmn | 新生代內(nèi)存大小,官方推薦為整個堆的3/8 | -Xmn512M |
| -XX:NewRatio=n | 設(shè)置新生代和年老代的比值。如: 3,表示年輕代與年老代比值為1:3,年輕代占整個年輕代年老代和的1/4 | -XX:NewRatio=3 |
| -XX:SurvivorRatio=n | 年輕代中Eden區(qū)與兩個Survivor區(qū)的比值。注意Survivor區(qū)有兩個。如: 8,表示Eden:Survivor=8:1:1,一個Survivor區(qū)占整個年輕代的1/10 | -XX:SurvivorRatio=8 |
| -Xss | 線程堆棧大小,jdk1.5及之后默認1M,之前默認256k | -Xss512k |
| -XX:PermSize=n | 永久代初始值,默認為物理內(nèi)存的1/64 | -XX:PermSize=128M |
| -XX:MaxPermSize=n | 永久代最大值,默認為物理內(nèi)存的1/4 | -XX:MaxPermSize=256M |
| -verbose:class | 在控制臺打印類加載信息 | |
| -verbose:gc | 在控制臺打印垃圾回收日志 | |
| -XX:+PrintGC | 打印GC日志,內(nèi)容簡單 | |
| -XX:+PrintGCDetails | 打印GC日志,內(nèi)容詳細 | |
| -XX:+PrintGCDateStamps | 在GC日志中添加時間戳 | |
| -Xloggc:filename | 指定gc日志路徑 | -Xloggc:/data/jvm/gc.log |
| -XX:+DisableExplicitGC | 關(guān)閉System.gc() | |
| -XX:+UseBiasedLocking | 自旋鎖機制的性能改善 | |
| -XX:PretenureSizeThreshold | 對象超過多大是直接在舊生代分配,默認值 0 ,單位字節(jié) | |
| -XX:TLABWasteTargetPercent | TLAB 占eden區(qū)的百分比 默認值 1% | |
| -XX:+CollectGen0First | fullGC 時是否先 youngGC ?默認值 false | |
| -XX:+PrintHeapAtGC | 打印 GC 前后的詳細堆棧信息 | |
| -XX:ParallelGCThreads=n | 設(shè)置并行收集器時使用的CPU數(shù)。此值最好配置與處理器數(shù)目相等,同樣適用于CMS | -XX:ParallelGCThreads=4 |
年輕代
| 參數(shù) | 說明 |
|---|---|
| -XX:+UseSerialGC | 年輕代設(shè)置串行收集器Serial |
| -XX:+UseParallelGC | 年輕代設(shè)置并行收集器Parallel Scavenge |
| -XX:UseParNewGC | 啟用ParNew收集器 |
| -XX:MaxTenuringThreshold | 幾次 youngGC 后會被分到老年代,默認是15次 |
| -XX:MaxGCPauseMillis=n | 年輕代垃圾回收的最長時間,如果無法滿足此時間,JVM會自動調(diào)整年輕代大小,以滿足此值 |
老年代
| 參數(shù) | 說明 |
|---|---|
| -XX:+UseParallelOldGC | 設(shè)置老年代為并行收集器ParallelOld收集器 |
| -XX:+UseConcMarkSweepGC | 設(shè)置老年代并發(fā)收集器CMS,且默認使用parNew作為新生代的垃圾回收 |
| -XX+UseCMSCompactAtFullCollection | fullGC過后,開啟對老年代的內(nèi)存壓縮,我們知道CMS使用的標記清除算法,會產(chǎn)生內(nèi)存碎片,所以需要內(nèi)存壓縮 |
| -XX:CMSFullGCsBeforeCompaction=n | 經(jīng)過幾次FullGC后進行內(nèi)存壓縮,默認是 0 |
| -XX:ParallelCMSThreads=n | CMS 過程并發(fā)線程數(shù) |
| -XX:+CMSParallelInitialMarkEnabled | 為了減少 CMS 初始標志暫停的時間,開啟并行標志 |
| -XX:+CMSParallelRemarkEnabled | 為了減少 CMS 第二次暫停的時間,開啟并行remark |
| -XX:+CMSScavengeBeforeRemark | 如果 CMS remark 暫停時間過長的話,可以開啟該選項,強制remark之前開始一次minor gc,減少remark的暫停時間,但是在remark之后也將立即開始又一次minor gc |
G1 特有參數(shù)
| 參數(shù) | 說明 |
|---|---|
| -XX:+UseG1GC | 使用 G1 (Garbage First) 垃圾收集器 |
| -XX:InitiatingHeapOccupancyPercent | 老年代占用空間達到整堆內(nèi)存閾值(默認45%),則執(zhí)行新生代和老年代的混合收集(MixedGC),比如我們之前說的堆默認有2048個region,如果有接近1000個region都是老年代的region,則可能 就要觸發(fā)MixedGC了 |
| -XX:MaxGCPauseMillis | 目標暫停時間(默認200ms) 也就是垃圾回收的時候允許停頓的時間 |
| -XX:G1MixedGCCountTarget | 在一次回收過程中指定做幾次篩選回收(默認8次),在最后一個篩選回收階段可以回收一 會,然后暫停回收,恢復(fù)系統(tǒng)運行,一會再開始回收,這樣可以讓系統(tǒng)不至于單次停頓時間過長 |
| -XX:G1HeapWastePercent | (默認5%) 在混合回收時,一旦空閑出來的Region數(shù)量達到了堆內(nèi)存的5%,此時就會立 即停止混合回收,意味著本次混合回收就結(jié)束了 |
| -XX:ConcGCThreads=n | 并發(fā)垃圾收集器使用的線程數(shù)量. 默認值隨JVM運行的平臺不同而不同 |
歡迎指正文中錯誤
參考文章
JVM(四)分代垃圾回收機制和垃圾回收算法?[1] JVM(五) GC 底層細節(jié)[2] ZGC設(shè)計與實現(xiàn)[3] ?炸了!一口氣問了我18個JVM問題 concurrent mode failure[4] 老大難的Java GC原理和調(diào)優(yōu),看這篇就夠了[5] ?Java中9種常見的CMS GC問題分析與解決 從實際案例聊聊Java應(yīng)用的GC優(yōu)化[6] JDK 11 ZGC簡介[7] JVM - 解讀GC中的 Safe Point & Safe Region[8]
參考資料
JVM(四)分代垃圾回收機制和垃圾回收算法?: https://www.cnblogs.com/mouren/p/14361439.html
[2]JVM(五) GC 底層細節(jié): https://www.cnblogs.com/mouren/p/14387192.html
[3]ZGC設(shè)計與實現(xiàn): 書籍
[4]concurrent mode failure: https://blog.csdn.net/muzhixi/article/details/105274542
[5]老大難的Java GC原理和調(diào)優(yōu),看這篇就夠了: https://www.cnblogs.com/caison/p/11641791.html
[6]從實際案例聊聊Java應(yīng)用的GC優(yōu)化: https://tech.meituan.com/2017/12/29/jvm-optimize.html
[7]JDK 11 ZGC簡介: https://www.cnblogs.com/ctgulong/p/9742434.html
[8]JVM - 解讀GC中的 Safe Point & Safe Region: https://blog.csdn.net/yangshangwei/article/details/107119177
