後端調優基礎——GC調優

GC類型

  • Minor GC :清理新生代,Minor GC是最頻繁觸發的GC,速度也最快的,主要工作原理是:
    對象在young區的eden創建,當eden空間滿後觸發Minor GC,將還存活的對象複製到一個survivor0中,另一個survivor1也會將對象複製過去,然後對eden和survivor1進行全部清理,survivor0和survivor1就這樣不斷交替,總有一個是空着的,當對象放不下或者是對象年齡足夠老(默認15)會將其放入Old區,由此可見Minor GC時不但會清理對象,還會將對象放入Old區。

可以通過設置-XX:MaxTenuringThreshold=n來指定對象經過多少次Minor GC後就進入Old 區,默認15
可以通過設置-XX:SurvivorRatio=n設置Survivor區和eden區的比例,如-XX:SurvivorRatio=8 那麼比例就是8:1:1 ,沒有設置的話就以-XX:InitialSurvivorRatio=n爲默認設置,這個值默認是8

  • Major GC :清理老年代,Major GC比Minor GC慢十倍以上,單獨觸發Major GC只有CMS和G1有這個能力,因爲cms和G1處理老年代不需要觸發full gc,其他的老年代回收器回收老年代需要觸發full gc。
  • Full GC: 清理整個堆空間,包括新生代、永久代和老年代,它會啓動老年代收集器和新生代收集器一起工作,全程Stop The World,儘量避免Full GC
Full gc:

full gc是對新生代,老年代、永久久代的統一回收,由於是對整個空間的回收,並且會觸發系統的停頓(stop-the-world),因此應當儘量的減少系統full gc的次數。

觸發的full gc的幾個條件
  • 老年代空間不足:新生代如果容量不足會將對象放到老年代,老年代空間不足是會觸發full gc,通過-Xmn設置新生代大小,也可以通過-XX:NewRatio=n設置比例,默認值是2,老年代:新生代=》2:1
  • 永久代空間不足:永久代在jdk7是存放在堆中的,可以通過-XX:PermSize=n-XX:MaxPermSize=n設置大。
    在jdk8中叫做元空間,使用的是計算機的本地內存通過-XX:MaxMetaspaceSize=n設置大小,如果不設置,默認最大內存大小是計算機的本地內存,由於運行時常量池在方法區中,而永久代又是方法區的實現,所以運行時常量池隨着jdk8也移動到了本地內存,但是無論是jdk7還是jdk8,字符串常量池還是在堆中,字符串常量的創建是需要消耗堆內存的
  • CMS 產生碎片過多已經扛不住壓力了就會調用full gc進行整合:可以通過-XX:CMSFullGCsBeforeCompaction=n設置要執行多少次full GC纔會做壓縮。默認是0,也就是每次full gc時就會對空間碎片進行整理,在默認配置下如果碎片過多CMS GC頂不住了,就要轉入full GC的時候都會做壓縮。(如果Full GC比較頻繁,那麼就不能每次都整理內存空間,不然積少成多,停頓的時間也是很可觀的,此時就要調大該參數,讓CMS在經過多次Full GC後再對內存空間進行壓縮整理,而如果Full GC發生的不頻繁,間隔時間較長,就可以設置成每次Full GC後都會對內存空間進行壓縮整理,影響也不大。)
  • CMS GC時出現了promotion failed和concurrent mode failure:
    1、promotion failed意思是晉升失敗是由於新生代把一些對象往老年代扔,然後老年代空間不足則拋出“promotion failed”,觸發full gc,可能的原因是:Survivor空間過小或者老年代空間小或者碎片多,或者兩者同時發生
    2、concurrent mode failure是CMS設置啓動的老年代內存佔比閾值過高,所以導致系統無法預留足夠的空間滿足程序需求,就會出現concurrent mode failure,啓動擔保機制,老年代增長過快觸發full gc進行清理,解決方法是降低觸發CMS的閥值,使用-XX:CMSInitiatingOccupancyFraction調低閾值,默認值是68,可以調到50
  • 統計得到新生代minor gc時晉升到老年代的平均大小大於老生代剩餘空間
    原因有幾個:1、代碼問題大量大對象直接進入老年代 2、老年代空間不足,通過-XX:NewRatio=n可以調整老年代和年輕的堆比例
  • 代碼直接調用System.gc()會建議系統調用full gc:在GC日誌中會顯示爲[Full GC(System),可以開啓-XX:-DisableExplicitGC禁止此類full gc

GC日誌

通過設置系統參數:-XX:+PrintGCDetails可以GC日誌打印到控制檯,以下幾個命令可以設置GC日誌

    -XX:+PrintGCTimeStamps 輸出GC的時間戳(以基準時間的形式)
    -XX:+PrintGCDateStamps 輸出GC的時間戳(以日期的形式,如 2020-10-23T21:53:59.234+0800)
    -XX:+PrintGCDetails   打印出GC的詳細信息
    -verbose:gc  開啓gc日誌   開啓這個按鈕後就算是使用了+PrintGCDetails也無法在控制檯看到,因爲打印的內容會到日誌文件中
    -Xloggc:d:/gc.log   gc日誌的存放位置
實戰:

運行這段代碼,由於堆內存被這些常量佔滿,馬上就會觸發gc了

public class Tzb {
    public static void main(String[] args) {
        while (true){
            String str=System.currentTimeMillis()+ UUID.randomUUID().toString();
            str.intern();
        }
    }
}

打印了下列的內容,我把它們一部分複製出來

-XX:InitialHeapSize=1048576 -XX:MaxHeapSize=1048576 -XX:+PrintCommandLineFlags -XX:+PrintGCDetails 
-XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC 
---------------------------------------------------------------------------------------------------------------------------------
[GC (Allocation Failure) [PSYoungGen: 505K->488K(1024K)] 505K->488K(1536K), 0.0006483 secs]
 [Times: user=0.00 sys=0.00, real=0.00 secs] 
---------------------------------------------------------------------------------------------------------------------------------
[Full GC (Ergonomics) [PSYoungGen: 488K->441K(1024K)] [ParOldGen: 400K->318K(512K)] 888K->759K(1536K), 
[Metaspace: 4010K->4010K(1056768K)], 0.0033631 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

上面的有三部分我用虛線標出來了行中

  • 第一部分:我使用了-XX:+PrintCommandLineFlags命令打印的部分jvm參數信息:初始化堆大小,最大堆大小等信息,還有-XX:+UseParallelGC使用的是Parallel Scavenge收集器

  • 第二部分(重點):非full gc 信息

1、PSYoungGen:新生代GC區域的信息,這個新生代區域的名字會跟着收集器的不同而改變,ParNew收集器叫做ParNew,Serial叫做DefNew,Parallel Scavenge叫做PSYoungGen,由於我使用的是Parallel Scavenge收集器所以新生代顯示爲PSYoungGen
2、[PSYoungGen:488K->441K(1024K)]:“GC前該內存區域已使用容量->GC後該內存區域已使用容量(該內存區域可用總容量,這裏Eden+Survivor0,另外一個由於是進行復制用的,所以不計入容量)”
3、505K->488K(1536K), 0.0006483 secs:“GC前Java堆已使用容量->GC後Java堆已使用容量(Java堆總容量),“0.0006483 secs”表示該內存區域GC所佔用的時間,單位是秒”
4、[Times: user=0.00 sys=0.00, real=0.00 secs]:這裏面的user、sys和real與Linux的time命令所輸出的時間含義一致,分別代表用戶態消耗的CPU時間、內核態消耗的CPU事件和操作從開始到結束所經過的牆鍾時間(Wall Clock Time)。CPU時間與牆鍾時間的區別是,牆鍾時間包括各種非運算的等待耗時,例如等待磁盤I/O、等待線程阻塞,而CPU時間不包括這些耗時,但當系統有多CPU或者多核的話,多線程操作會疊加這些CPU時間,所以讀者看到user或sys時間超過real時間是完全正常的

  • 第三部分(重點):full gc 信息

觸發full gc會嚴重影響程序的運行性能,因爲發生了牛批的stop-the-world
1、PSYoungGen:新生代GC區域發生了full gc
2、ParOldGen:發生老年代的區域,由於收集器不同老年代的區域名稱也會不同,Serial old叫做Tenured,Parallel Old叫做ParOldGen,由於我使用的是Parallel Scavenge收集器會自動激活Parallel Old收集器所以老年代叫做ParOldGen
3、Metaspace:元空間,jdk7叫做Perm也就是方法區也成爲永久代,jdk8後把方法區移除了,變成了元空間也就是這個Metaspace

下面列出gc日誌中各個新生代收集器默認配合的老年代收集器及各自對應的新生代區域和老年代區域的名字:

  • 1、Serial:DefNew ——> Serial Old:Tenured
  • 2、ParNew:ParNew ——> Serial Old:Tenured
  • 3、Parallel Scavenge:PSYoungGen ——> parNew Old:ParOldGen
  • 4、CMS是比較特殊的使用的標記清除法,只能ParNew、Serial配合,一般是選擇ParNew與CMS配合,CMS是多線程的老年代收集器有很低的停頓時間,所以性能也比較不錯,可惜它不能和Parallel Scavenge配合,所以CMS不是默認使用的收集器,要使用必須自己配,選擇了CMS就會默認開啓ParNew和Serial Old,啓動擔保機制,後面我會專門講它的
  • 5、G1是老少通喫的,不用和其他收集器配合,並且性能高停頓時間少,到jdk9後是默認的收集器,越到後面越牛批

這是目前官方推薦的配合主流

控制檯這玩意在線上能看?試一下生產日誌文件?

打開後長這樣:


這裏有一個GC日誌分析工具,在線的,自己上去玩吧
https://gceasy.io/

GC收集器

  • 併發:收集器和用戶程序同時進行
  • 並行:暫停用戶程序使用多線程執行GC

Serial

Serial是串行新生代收集器,client的默認收集器,Serial Old是老年代的Serial,這兩個特點除了工作區域與算法不一樣其他沒啥區別,一般適用於小型應用和單處理器,單核GC效率較高,但是會發生Stop-The-World,可以與CMS,Serial Old使用,由於沒有線程開銷,所以在單核情況下性能無敵(現在沒什麼服務器是單核了吧!!)

  • -XX:+UseSerialGC:如何沒有配置其他的老年代,這個配置可以同時打開Serial和Serial Old,否則只打開Serial

ParNew(jdk9已經不支持)

是Serial的多線程版,本並行收集器,除了多線程與Serial沒什麼區別,這兩款是共用了絕大部分的代碼,所以特點幾乎沒什麼區別,一樣會發生Stop-The-World。許多Server選它只是爲了和CMS配合而已。(如何不是爲了配合CMS最好不要用這個收集器)

  • -XX:+UseParNewGC:如何沒有配置其他的老年代,這個配置可以同時打開ParNew和Serial Old,否則只打開ParNew
  • -XX:ParallelGCThreads=n:設置並行收集器收集時使用的線程數,如果cpu核數小於8最好與CPU數目相等,如果大於8可以設置爲3+(cpu core*5)/8。

Parallel Scavenge

並行收集器,這個收集器比較重要,是jdk7和jdk8 Server端默認的新生代收集器,Parallel Scavenge的特點是多線程,追求高吞吐量(吞吐量就是假如系統運行100分鐘,收集器工作1分鐘,吞吐量爲99%),它有自適應調節的功能,通過收集系統的信息動態調節自身的參數,達到高吞吐的目的,吞吐高代表CUP使用率高,優點很明顯但是缺點也很明顯,它無法與老年代的CMS適配,同樣是高性能的CMS追求的是最短的停頓時間,魚和熊掌不可兼得,jvm還是選擇了Parallel Scavenge作爲默認Server的收集器,爲了配合它的高吞吐,jvm還專門弄了一個ParNew Old,它是ParNew的老年版本。(這麼牛批但是依然會發生Stop-The-World)

  • -XX:+UseParallelGC:通過這個配置,可以同時打開Parallel Scavenge和ParNew Old
  • -XX:ParallelGCThreads=n:設置並行收集器收集時使用的線程數,如果cpu核數小於8最好與CPU數目相等,如果大於8可以設置爲3+(cpu core*5)/8。
  • -XX:MaxGCPauseMillis=n:設置新生代每次並行垃圾回收的最大暫停時間。設置這個值回收器會盡可能的去實現,jvm會根據這個停頓時間,把掃描的範圍縮小成一個小的堆進行GC掃描達到縮短停頓的目的
  • -XX:GCTimeRatio=n:設置垃圾回收時間佔程序運行時間的百分比。公式爲1/(1+n)。默認是99
  • -XX:+UseAdaptiveSizePolicy:自適應策略,自動選擇新生代區大小和相應的Survivor區比例、吞吐量(GCTimeRatio),停頓時間(MaxGCPauseMillis),這個是在調優比較複雜的場合下可以使用
  • -XX:+ScavengeBeforeFullGC:在full gc前觸發一次minor gc

CMS(jdk9已經不建議使用,被G1取代)

這個比較牛批的老年代收集器,使用的是標記清除法,由於追求的是最短停頓時間,所以可以帶給用戶良好的體驗,非常適合互聯網和B/S的服務器。
一般老年代和永久代的回收是需要觸發full gc的,但是CMS可以在不觸發full gc的情況下單獨對老年代和永久代進行gc,但是它的gc是需要檢查永久代和老年代空間使用率默認是達到92%才啓動,還要通過-XX:+CMSInitiatingOccupancyFraction=n設置,gc有7個步驟

1、初始標記(STW initial mark),會Stop-The-World
2、併發標記(Concurrent marking),併發操作,不會Stop-The-World
3、併發預清理(Concurrent precleaning),併發操作,不會Stop-The-World
5、重新標記(Final remark),會Stop-The-World
6、併發清理(Concurrent sweeping),併發操作,不會Stop-The-World
7、併發重置(Concurrent reset),併發操作,不會Stop-The-World

我們看到只是標記的部分都是會發生Stop-The-World的,但是標記的操作是非常短的,可以忽略不計的,所以CMS能做到最短的停頓時間,效率極高,但是缺點就是無法與Parallel Scavenge適配,CMS啓動時會同時默認啓動ParNew、Serial Old,爲了防止SMC的回收失敗,使用了CMS+ParNew+Serial Old的擔保機制

擔保機制:

由於CMS爲了降低停頓,有一定的併發,啓動時需要預留足夠的內存給用戶線程,所以CMS需要在內存空間滿之前就得啓動,jdk5默認是當老年代65%時啓動,而jdk6以後是默認92%,還要通過-XX:+CMSInitiatingOccupancyFraction設置更高的啓百分比,如果垃圾生產的太快了或者閾值太高,在CMS運行期間發現無法預留足夠的內存,就會出現concurrent mode failure而運行異常,這時JVM就會啓動備用的Serial Old去處理老年代垃圾,所以CMS+ParNew+Serial Old的擔保機制是爲了防止CMS的concurrent mode failure的異常

  • -XX:+UseConcMarkSweepGC:使用此命令可以同時開啓CMS、ParNew、Serial Old。
  • -XX:ParallelCMSThreads=n: 設定 CMS 的併發線程數量。默認線程數是(ParallelGCThreads+3)/4。意味着當默認條件下ParallelGCThreads爲4時,只有一個線程併發
  • -XX:+CMSParallelRemarkEnabled:採用並行標記方式降低Stop-The-World的時間。
  • -XX:+UseCMSCompactAtFullCollection:此開關默認開啓,用於消除碎片。每一次FULL GC都伴隨一次碎片回收
  • -XX:CMSFullGCsBeforeCompaction=n:設置在執行多少次Full GC後對內存空間進行壓縮整理。設置這個的前提是UseCMSCompactAtFullCollection開啓
  • -XX:+CMSInitiatingOccupancyFraction:設置 CMS 收集器在老年代空間被使用多少後觸發,默認爲 68%,設置得太高會觸發Full gc影響性能。
  • -XX:+CMSClassUnloadingEnabled:允許對類元數據進行回收。也就是允許CMS回收方法區
  • -XX:CMSInitatingPermOccupancyFraction=n:當永久區佔用率達到這一百分比後,啓動 CMS 回收 (前提是-XX:+CMSClassUnloadingEnabled激活了)。
  • -XX:UseCMSInitatingOccupancyOnly:表示只在到達閾值的時候,才進行 CMS 回收。

CMS示例, 如圖:我沒有選擇任何的新生代收集器,只是開啓了CMS

我已經圈出來了,可以只要開啓CMS就會自動選擇ParNew

我將CMS的一部分複製下來

[GC (CMS Initial Mark) [1 CMS-initial-mark: 723K(768K)] 742K(1920K), 0.0002026 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
---------------------------------------------------------------------------------------------------------------------------------
[CMS-concurrent-mark-start]
[CMS-concurrent-mark: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
---------------------------------------------------------------------------------------------------------------------------------
[CMS-concurrent-preclean-start]
[CMS-concurrent-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
---------------------------------------------------------------------------------------------------------------------------------
[GC (CMS Final Remark) [YG occupancy: 75 K (1152 K)][Rescan (parallel) , 0.0001481 secs][weak refs processing, 0.0000063 secs]
[class unloading, 0.0001892 secs][scrub symbol table, 0.0003164 secs][scrub string table, 0.0000845 secs]
---------------------------------------------------------------------------------------------------------------------------------
[1 CMS-remark: 723K(768K)] 799K(1920K), 0.0007957 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
---------------------------------------------------------------------------------------------------------------------------------
[CMS-concurrent-sweep-start]
[CMS-concurrent-sweep: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
---------------------------------------------------------------------------------------------------------------------------------
[CMS-concurrent-reset-start]
[CMS-concurrent-reset: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
---------------------------------------------------------------------------------------------------------------------------------

一共有7部分,對應着上面列出的CMS的7個步驟

G1

G1收集器是JDK9默認的收集器,jdk7和jdk8中都可以用它,到了jdk9才正式成爲默認,G1完全取代了CMS,針對CMS的碎片化進行了改進,使用G1不再需要與其他的收集器配合,原因是在於G1對之前的內存結構做了非常大的改動,不再把新生代和老年代分別分配成一整塊一整塊的大區域,而是把之前的老年代和新生代的Eden,survivior分割成一小塊一小塊的Region放在同一片堆區域,每塊Region最大是32M,堆空間最多存放2048個Region,就是最多是60G到70G,G1基於複製算法,高效的整理剩餘內存,而不需要管理內存碎片,
G1有以下的特點:

  • 1、並行與併發:G1能充分利用CPU、多核環境下的硬件優勢,使用多個CPU來縮短stop-The-World停頓時間。部分其他收集器原本需要停頓Java線程執行的GC動作,G1收集器仍然可以通過併發的方式讓java程序繼續執行。
  • 2、空間整合:G1收集器從整體上看是採用標記整理算法,主要是把每一塊當做一個標記單位,但是從局部也就每一塊Region是基於複製算法的,把需要清理的區域中存活的對象複製到其他區域,所以不會產生碎片,分配大對象時不會因爲找不到足夠大的連續內存空間而觸發下一次GC
  • 3、可預測停頓:降低停頓時間是CMS和G1共同的關注點,但是G1除了追求低停頓外,還能建立可預測的停頓時間模型,能讓使用者明確指定在一個長度爲N毫秒時間片段內,消耗在垃圾收集上的時間不得超過N毫秒。
  • 4、分代收集:物理結構改變了,但是仍然保留着之前的分代概念,收集垃圾的原理依舊是分代收集

G1的收集算法也比較獨特,採用了分代並且分區的算法,分代就是根據不同的塊的特點,例如新生代的塊就會使用複製算法,老年代的塊使用標記壓縮,並且每個塊都分配一個remember set集合記錄所屬塊和其他塊的對象引用關係,通過檢查這個集合找出垃圾對象,避免了掃描整個確定可達關係。

一般來說,空間越大掃描的範圍就越大,停頓時間就長,但是進行分塊可以減少每次掃描整個堆空間的時間

內存結構如圖,其中Humongous是大對象,大對象直接進入Humongous的Region

同時還有一個併發標記過程記錄的G區,G區是垃圾對象比率較高的區域,混合回收會優先清理的區域

G1的GC模式兩種:
  • 1、Young GC:G1對新生代的 Region進行GC
  • 2、Mixed GC:根據併發標記過程統計得出收集收益高的Region進行GC。在用戶指定的開銷目標範圍內儘可能選擇收益高 Region,以此來控制Mixed GC的時間開銷,達到可預測停頓,這裏處理的Region就G區,老年代和新生代的Region都可能成爲G區
G1的工作流程主要分兩個階段:併發標記和混合GC
1、併發標記:
  • 初始化標記:這個過程會產生STW,標記可達對象,同時進行一次young gc清理eden區
[GC pause (G1 Evacuation Pause) (young) (initial-mark), 0.0013482 secs]
  • 根區域掃描:併發掃描survivor區在old區的可達對象,這個過程中young gc會停止
[GC concurrent-root-region-scan-start]
[GC concurrent-root-region-scan-end, 0.0000189 secs]

[Ext Root Scanning (ms): Min: 0.0, Avg: 0.1, Max: 0.2, Diff: 0.2, Sum: 0.7]
  • 併發標記:併發標記堆中還存活的對象,這個過程可以被young gc打斷
[GC concurrent-mark-start]
  • 重新標記:這個過程會產生STW,暫停程序重新標記存活的對象
  • 獨佔清理:會STW,計算各個區域的存活對象和GC回收比例,並做排序,標記可供混合回收的區域,同時更新remember set(下面是更新rs和掃描rs的gc日誌)
[Update RS (ms): Min: 0.0, Avg: 0.3, Max: 0.4, Diff: 0.4, Sum: 1.7]
         [Processed Buffers: Min: 0, Avg: 0.8, Max: 1, Diff: 1, Sum: 5]
      [Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
  • 併發清理:清理空閒區域
2、混合清理:

併發標記完成後,G1會根據設置的停頓時間,優先選擇清理性價比高的區域,也就是G區

full gc

和CMS一樣,G1也是個併發的回收器,當不足以提供足夠內存給用戶程序時同樣會觸發full gc

[GC concurrent-mark-start]
[Full GC (Allocation Failure)  1712K->690K(2048K), 0.0023713 secs]
   [Eden: 0.0B(1024.0K)->0.0B(1024.0K) Survivors: 0.0B->0.0B Heap: 1712.1K(2048.0K)->690.3K(2048.0K)], [Metaspace: 3469K->3469K(1056768K)]
 [Times: user=0.02 sys=0.00, real=0.00 secs] 
[GC concurrent-mark-abort]
  • -XX:+UseG1GC : 讓 JVM使用G1垃圾收集器, jdk9被設爲默認垃圾收集器;所以如果你的版本比較新則不再需要使用該參數
  • -XX:MaxGCPauseMillis=200: 設置最大GC停頓時間指標,JVM會盡力實現,但不保證. 默認值爲200毫秒.
  • -XX:InitiatingHeapOccupancyPercent=45: 如果整個堆內存的45%的時候啓動G1,此時會觸發併發標記和一次mixGc。值爲0則表示一直執行GC循環, 默認值爲45,設置過大會導致full gc。
  • -XX:G1MixedGCLiveThresholdPercent=n:默認值是85%,確定要回收的Region的時候,必須是存活對象低於85%的Region纔可以回收。
  • -XX:G1ReservePercent=n: 設置堆的空閒內存預留百分比,以降低內存溢出的風險. 默認值是 10,基本不變.
  • -XX:ConcGCThreads=n: 併發垃圾收集器使用的線程數量. 默認與cup數量一致
  • -XX:MetaspaceSize=256M: 初始化元空間,默認20M。
  • -XX:MaxMetaspaceSize=512M: 最大元空間
G1收集器演示:

G1真的比較複雜,算法和機制都比較難,日誌多了很多

GC篇總結:

  • 停頓時間參數,如:G1的MaxGCPauseMillis,parallel的MaxGCPauseMillis如果調低了GC次數就會上升。
  • 線程參數,如:parallel的ParallelGCThreads,CMS的ParallelCMSThreads,G1的ConcGCThreads調高在垃圾生產多並且cup緊張的場景會消耗cpu性能
  • 堆空間參數:通過Xmx Xms調整堆的大小,堆越多gc次數自然就會減少,但是堆空間大反而會增長gc時間,可以根據情況設置停頓時間去縮短每次gc時間、或者使survivor減小有效的減少複製空間減少複製操作讓對象盡塊進入老年代然後使用CMS進行併發處理、或者直接使用G1收集器
  • 啓動閾值:G1的InitiatingHeapOccupancyPercent,CMS的CMSInitiatingOccupancyFraction啓動閾值過高會容易觸發full gc,啓動閾值過低會增加普通gc次數
  • System.gc:如果代碼中有頻繁使用 System.gc(),那麼需要使用-XX:-DisableExplicitGC禁止full gc
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章