垃圾收集器及GC調優
串行回收器
新生代串行回收器
特點
它僅僅使用單線程進行垃圾回收
它是獨佔式的垃圾回收
進行垃圾回收時, Java應用程序中的線程都需要暫停(Stop-The-World)
使用複製算法
適合CPU等硬件不是很好的場合
設置參數
-XX:+UseSerialGC 指定新生使用新生代串行收集器和老年代串行收集器, 當以client模式運行時, 它是默認的垃圾收集器
老年代串行回收器
特點
同新生代串行回收器一樣, 單線程, 獨佔式的垃圾回收器
通常老年代垃圾回收比新生代回收要更長時間, 所以可能會使應用程序停頓較長時間
設置參數
-XX:+UseSerialGC 新生代, 老年代都使用串行回收器
-XX:+UseParNewGC 新生代使用ParNew回收器, 老年代使用串行回收器
-XX:+UseParallelGC 新生代使用ParallelGC回收器, 老年代使用串行回收器
並行回收器
新生代ParNew回收器
特點
將串行回收多線程化
使用複製算法
垃圾回收時, 應用程序仍會暫停, 只不過由於是多線程回收, 在多核CPU上,回收效率會高於串行回收器, 反之在單核CPU, 效率會不如串行回收器
設置參數
-XX:+UseParNewGC 新生代使用ParNew回收器, 老年代使用串行回收器
-XX:+UseConcMarkSweepGC 新生代使用ParNew回收器, 老年代使用CMS回收器
-XX:ParallelGCThreads=n 指回ParNew回收器工作時的線程數量, cpu核數小時8時, 其值等於cpu數量, 高於8時,可以使用公式(3+((5*CPU_count)/8))
新生代ParallelGC回收器
特點
同ParNew回收器一樣, 不同的地方在於,它非常關注系統的吞吐量(通過參數控制)
使用複製算法
支持自適應的GC調節策略
設置參數
-XX:+UseParallelGC 新生代用ParallelGC回收器, 老年代使用串行回收器
-XX:+UseParallelOldGC 新生代用ParallelGC回收器, 老年代使用ParallelOldGC回收器系統吞吐量的控制:
-XX:MaxGCPauseMillis=n(單位ms) 設置垃圾回收的最大停頓時間,
-XX:GCTimeRatio=n(n在0-100之間) 設置吞吐量的大小, 假設值爲n, 那系統將花費不超過1/(n+1)的時間用於垃圾回收
-XX:+UseAdaptiveSizePolicy 打開自適應GC策略, 在這種模式下, 新生代的大小, eden,survivior的比例, 晉升老年代的對象年齡等參數會被自動調整,以達到堆大小, 吞吐量, 停頓時間之間的平衡點
老年代ParallelOldGC回收器
特點
同新生代的ParallelGC回收器一樣, 是屬於老年代的關注吞吐量的多線程併發回收器
使用標記壓縮算法
設置參數
-XX:+UseParallelOldGC 新生代用ParallelGC回收器, 老年代使用ParallelOldGC回收器, 是非常關注系統吞吐量的回收器組合, 適合用於對吞吐量要求較高的系統
-XX:ParallelGCThreads=n 指回ParNew回收器工作時的線程數量, cpu核數小時8時, 其值等於cpu數量, 高於8時, 可以使用公式(3+((5*CPU_count)/8))
CMS回收器(Concurrent Mark Sweep,併發標記清除)
老年代的併發回收器
特點
是併發回收, 非獨佔式的回收器, 大部分時候應用程序不會停止運行
針對年老代的回收器
使用併發標記清除算法, 因此回收後會有內存碎片, 可以使參數設置進行內存碎片的壓縮整理
與ParallelGC和ParallelOldGC不同, CMS主要關注系統停頓時間
主要步驟
初始標記
併發標記
預清理
重新標記
併發清理
併發重置
注:初始標記與重新標記是獨佔系統資源的,不能與用戶線程一起執行,而其它階段則可以與用戶線程一起執行
設置參數
-XX:-CMSPrecleaningEnabled 關閉預清理, 不進行預清理, 默認在併發標記後, 會有一個預清理的操作,可減少停頓時間
-XX:+UseConcMarkSweepGC 老年代使用CMS回收器, 新生代使用ParNew回收器
-XX:ConcGCThreads=n 設置併發線程數量,
-XX:ParallelCMSThreads=n 同上, 設置併發線程數量,
-XX:CMSInitiatingOccupancyFraction=n 指定老年代回收閥值, 即當老年代內存使用率達到這個值時, 會執行一次CMS回收,默認值爲68, 設置技巧: (Xmx-Xmn)*(100-CMSInitiatingOccupancyFraction)/100)>=Xmn
-XX:+UseCMSCompactAtFullCollection 開啓內存碎片的整理, 即當CMS垃圾回收完成後, 進行一次內存碎片整理, 要注意內存碎片的整理並不是併發進行的, 因此可能會引起程序停頓
-XX:CMSFullGCsBeforeCompation=n 用於指定進行多少次CMS回收後, 再進行一次內存壓縮
-XX:+CMSParallelRemarkEnabled 在使用UseParNewGC 的情況下, 儘量減少 mark 的時間
-XX:+UseCMSInitiatingOccupancyOnly 表示只有達到閥值時才進行CMS回收
-XX:+CMSConcurrentMTEnabled 當該標誌被啓用時,併發的CMS階段將以多線程執行,默認開啓
-XX:+CMSIncrementalMode 在增量模式下,CMS 收集器在併發階段,不會獨佔整個週期,
而會週期性的暫停,喚醒應用線程。收集器把併發階段工作,劃分爲片段,安排在次級(minor) 回收之間運行。這對需要低延遲,運行在少量CPU服務器上的應用很有用。
Class的回收(永久區的回收)
設置參數
-XX:+CMSClassUnloadingEnabled 開啓回收Perm區的內存, 默認情況下, 是需要觸發一次FullGC
-XX:CMSInitiatingPermOccupancyFraction=n 當永久區佔用率達到這個n值時,啓動CMS回收, 需上一個參數開啓的情況下使用
G1回收器(jdk1.7後全新的回收器, 用於取代CMS)
概述
特點
獨特的垃圾回收策略, 屬於分代垃圾回收器
使用分區算法, 不要求eden, 年輕代或老年代的空間都連續
並行性: 回收期間, 可由多個線程同時工作, 有效利用多核cpu資源
併發性: 與應用程序可交替執行, 部分工作可以和應用程序同時執行
分代GC: 分代收集器, 同時兼顧年輕代和老年代
空間整理: 回收過程中, 會進行適當對象移動, 減少空間碎片
可預見性: G1可選取部分區域進行回收, 可以縮小回收範圍, 減少全局停頓
主要步驟
1.新生代GC
2.併發標記週期
初始標記新生代GC(此時是並行, 應用程序會暫停止)–>根區域掃描–>併發標記–>重新標記(此時是並行, 應用程序會暫停止)–>獨佔清理(此時應用程序會暫停止)–>併發清理
3.混合回收這個階段即會執行正常的年輕代gc, 也會選取一些被標記的老年代區域進行回收, 同時處理新生代和年老輕
若需要, 會進行FullGC
4.混合GC時發生空間不足
在新生代GC時, survivor區和老年代無法容納倖存對象時
以上兩者都會導致一次FullGC產生
設置參數
-XX:+UseG1GC 打開G1收集器開關,
-XX:MaxGCPauseMillis=n 指定目標的最大停頓時間,任何一次停頓時間超過這個值, G1就會嘗試調整新生代和老年代的比例, 調整堆大小, 調整晉升年齡
-XX:ParallelGCThreads=n 用於設置並行回收時, GC的工作線程數量
-XX:InitiatingHeapOccpancyPercent=n 指定整個堆的使用率達到多少時, 執行一次併發標記週期, 默認45, 過大會導致併發標記週期遲遲不能啓動, 增加FullGC的可能, 過小會導致GC頻繁, 會導致應用程序性能有所下降
參數選項
選項 | 默認值 | 描述 |
---|---|---|
-XX:+UseG1GC | 默認關閉 | 使用G1垃圾處理器 |
-XX:MaxGCPauseMillis=n | 默認值:4294967295 | 設置並行收集最大暫停時間,這是一個理想目標,JVM將盡最大努力來實現它。 |
-XX:InitiatingHeapOccupancyPercent=n | 默認值:45 | 啓動一個併發垃圾收集週期所需要達到的整堆佔用比例。這個比例是指整個堆的佔用比例而不是某一個代(例如G1),如果這個值是0則代表‘持續做GC’。默認值是45 |
-XX:NewRatio=n | 默認值:2 | 設置年輕代和年老代的比值。例如:值爲3,則表示年輕代與年老代比值爲1:3,年輕代佔整個年輕代年老代和的1/4。 |
-XX:SurvivorRatio=n | 默認值:8 | 年輕代中Eden區與兩個Survivor區的比值。注意Survivor區有兩個。如:3,表示Eden:Survivor=3:2,一個Survivor區佔整個年輕代的1/5 |
-XX:MaxTenuringThreshold=n | 默認值:15 | 設置垃圾最大存活閥值。如果設置爲0的話,則年輕代對象不經過Survivor區,直接進入年老代。對於年老代比較多的應用,可以提高效率。如果將此值設置爲一個較大值,則年輕代對象會在Survivor區進行多次複製,這樣可以增加對象再年輕代的存活時間,增加在年輕代即被回收的概論。 |
-XX:ParallelGCThreads=n | 默認值:隨JVM運行平臺不同而異 | 配置並行收集器的線程數,即:同時多少個線程一起進行垃圾回收。此值最好配置與處理器數目相等。 |
-XX:ConcGCThreads=n | 默認值:隨JVM運行平臺不同而異 | 並行垃圾收集時,使用的線程數。默認值和JVM運行的平臺有關。 |
-XX:G1ReservePercent=n | 默認值:10 | 設置保留用來做假天花板以減少晉升(新生代對象晉升到老生代)失敗可能性的堆數目。 |
-XX:G1HeapRegionSize=n | 默認值根據堆大小而定 | 使用G1垃圾回收器,java堆被劃分成統一大小的區塊。這個選項設置每個區塊的大小。最小值是1Mb,最大值是32Mb。 |
其他GC相關的設置
System.gc()
(1)禁用System.gc()
-XX:+DisableExplicitGC 禁止程序中調用System.gc(), 加了此參數, 程序若有調用, 返回的空函數調用
System.gc()的調用, 會使用FullGC的方式回收整個堆而會忽略CMS或G1等相關回收器
(2)System.gc()使用併發回收
-XX:+ExplicitGCCinvokesConcurrent 使用併發方式處理顯示的gc, 即開啓後, System.gc()這種顯示GC纔會併發的回收, (CMS, G1)
並行GC前額外觸發的新生代GC
(1)使用並行回收器(UseParallelGC或者UseParallelOldGC)時, 會額外先觸發一個新生代GC, 目的是儘可能減少停頓時間
(2)若不需要這種特性, 可以使用以下參數去除
-XX:-ScavengeBeforeFullGC 即去除在FullGC之前的那次新生代GC, 原本默認值爲true
對象何時進入老年代
(1)當對象首次創建時, 會放在新生代的eden區, 若沒有GC的介入,會一直在eden區, GC後,是可能進入survivor區或者年老代
(2)當對象年齡達到一定的大小 ,就會離開年輕代, 進入老年代, 對象進入老年代的事件稱爲晉升, 而對象的年齡是由GC的次數決定的, 每一次GC,若對象沒有被回收, 則對象的年齡就會加1, 可以使用以下參數來控制新生代對象的最大年齡:
-XX:MaxTenuringThreshold=n 假設值爲n , 則新生代的對象最多經歷n次GC, 就能晉升到老年代, 但這個必不是晉升的必要條件
-XX:TargetSurvivorRatio=n 用於設置Survivor區的目標使用率,即當survivor區GC後使用率超過這個值, 就可能會使用較小的年齡作爲晉升年齡
(3)除年齡外, 對象體積也會影響對象的晉升的, 若對象體積太大, 新生代無法容納這個對象, 則這個對象可能就會直接晉升至老年代, 可通過以下參數使用對象直接晉升至老年代的閾值, 單位是byte
-XX:PretenureSizeThreshold 即對象的大小大於此值, 就會繞過新生代, 直接在老年代分配, 此參數只對串行回收器以及ParNew回收有效, 而對ParallelGC回收器無效
在TLAB上分配對象(Thread Local Allocation Buffer, 線程本地分配緩存)
(1)TLAB: TLAB是一個線程專用的內存分配區域, 虛擬機爲線程分配空間, 針對於體積不大的對象, 會優先使用TLAB, 這個可以加速對象的分配, TLAB是默認開啓的, 若要關閉可以使用以下參數關閉
-XX:-UseTLAB 關閉TLAB
-XX:+UseTLAB 開啓TLAB, 默認也是開啓的
-XX:+PrintTLAB 觀察TALB的使用情況
-XX:TLABRefillWasteFraction=n 設置一個比率n, 而refill_waste的值就是(TLAB_SIZE/n), 即TLAB空間較小, 大對象無法分配在TLAB,所以會直接分配到堆上,TLAB較小也很容易裝滿, 因此當TLAB的空間不夠分配一個新對象, 就會考慮廢棄當前TLAB空間還是直接分配到堆上, 就會使用此參數進行判斷, 小於refill_waste就允許廢棄, 而新建TLAB來分配對象,而大於refill_waste就直接在堆上分配, 默認是64
-XX:+ResizeTLAB 開啓TLAB自動調整大小, 默認是開啓的, 若要關閉把+號換成-號即可
-XX:TLABSize=n 設置一個TLAB的大小, 前提先關閉TLAB的自動調整