jvm層gc調優
jvm的記憶體結構
java 8 jvm官方文件
https://docs.oracle.com/javase/specs/jvms/se8/html/index.html
執行時資料區
官方文件:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5
執行時資料區有:方法區、虛擬機器棧、本地方法棧、堆、程式計數器
計數器pc register
jvm支援多執行緒同時執行,每一個執行緒都有自己的pc register,執行緒正在執行的方法叫做當前方法,如果是java程式碼pc register裡面存放的就是當前正在執行的指令的地址,如果是c程式碼,則為空
虛擬機器棧jvm stacks
java虛擬機器棧(java virtual machine stacks)是執行緒私有的,它的生命週期與執行緒相同。虛擬機器棧描述的是java方法執行的記憶體模型;每隔方法在執行的同事都會建立一個棧幀,用於儲存區域性變量表、運算元棧、動態連結、方法出口等資訊,每一個方法從呼叫直至執行完成的過程,就對應著一個棧幀在虛擬機器中入棧到出棧的過程。
堆heap
java堆是java虛擬機器所管理的記憶體中最大的一塊。堆是被所有執行緒共享的一塊記憶體區域,在虛擬機器啟動時穿件。此記憶體區域的唯一目的就是存放物件例項,幾乎所有的物件例項都在這裡分配記憶體。java堆可以處於物理上不連續的記憶體空間中,只要邏輯上是連續的就可以。
方法區Method Area
方法區域java堆一樣,是哥哥執行緒共享的記憶體區域,它用於儲存已被虛擬機器載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼等資料。雖然java虛擬機器規範把方法區描述為堆的一個邏輯部分,但是它卻又一個別名叫做non-heap(非堆),目的是與java堆區分開來。
常量池 run-time constant pool
執行時常量池是方法區的一部分。class檔案中除了有類的版本、欄位、方法、介面等描述資訊外,還有一項資訊是常量池(constant pool table),用於存放編譯期生成的各種字面量和符號引用,這部分內容將在類載入後進入方法區的執行時常量池中存放。
本地方法棧native method stacks
本地方法棧與虛擬機器棧所發揮的作用是非常相似的,他們之間的區別不過是虛擬機器棧為虛擬機器執行java方法(也就是位元組碼)服務,而本地方法棧則為用虛擬機器使用到的native方法服務。
堆區
堆區分為老年代old和新生代(Young),新生代又分為eden和s0和s1區。
非堆區
metaspace=class、package、method、field、位元組碼、常量池、符號引用等。
css:32位指標的class
通過新增虛擬機器引數:+UseCompressedClassPointers使用ccs,通過:jstat -gc pid檢視CCS
codecache:JIT編譯後的原生代碼、JNI使用的C程式碼
常用引數
-Xms -Xmx
-XX:NewSize -XX:MaxNewSize
-XX:NewSize -XX:MaxNewSize
-XX:NewRatio -XX:SurvivorRatio
-XX:MetaspaceSize -XX:MaxMetaspaceSize
-XX:+UseCompressedClassPointers
-XX:CompressedClassSpaceSize
-XX:InitialCodeCacheSize
-XX:ReservedCodeCacheSize
垃圾回收演算法
思想
列舉根節點,做可達性分析
根節點
類載入器、Thread、虛擬機器棧的本地變量表、static成員、常量引用、本地方法棧的變數等等
標記清除
演算法
演算法分為“標記”和“清除”兩個階段:首先標記處所有需要回收的物件,在標記完成後統一回收所有
缺點
效率不高。標記和清除兩個過程的效率都不高。產生碎片。碎片太多會導致提前GC
複製演算法
它將可用記憶體按容量分為大小相等的兩塊,每次只使用其中的一塊,當這一塊的記憶體用完了,就將還存活的物件複製到另外一塊上面,然後再把已使用過的記憶體空間一次清理掉。
優缺點
實現簡單。執行高效,但是空間利用率低。
標記整理
演算法
標記過程仍然與“標記-清除”演算法一樣,但後續步驟不是直接對可回收物件進行清理,而是讓所有存活的物件都向一端移動,然後直接清理掉端以外的記憶體
優缺點
沒有了記憶體碎片,但是整理記憶體比較耗時
分代回收演算法
Young區用複製演算法
Old區用標記清除或者標記整理
物件分配
物件優先在Eden區分配
大物件直接進入老年代:-XX:PretenureSizeThreshold
長期存活物件進入老年代:-XX:MaxTenuringThreshold -XX:+PrintTenuringDistribution -XX:TargetSurvivorRatio
垃圾收集器
知乎上優秀的回答:https://www.zhihu.com/question/35164211
垃圾收集器
序列收集器Serial:Serial、Serial Old
並行收集器Parallel:Parallel Scavenge、Parallel Old,吞吐量
併發收集器Concurrent:CMS、G1、停頓時間
並行vs併發
並行(Parallel):指多條垃圾收集執行緒並行工作,但此時使用者執行緒仍然處於等待狀態。適合科學計算、後臺處理等弱互動場景
併發(Concurrent):指使用者執行緒與垃圾收集執行緒同時執行(但不一定是並行的,可能會交替執行),垃圾收集執行緒在執行的時候不會停頓使用者程式的執行。適合對響應時間有要求的場景,比如web。
停頓時間vs吞吐量
停頓時間:垃圾收集器做垃圾回收中斷應用程式執行時間。-XX:MaxGCPauseMillis
吞吐量:花在垃圾收集的時候和花在應用時間的佔比 .
XX:GCTimeRatio=,垃圾收集的時間佔比:1/1+n
並行收集器
吞吐量優先
-XX:+UseParallelGC,-XX:+UseParallelOldGC
Server模式下的預設收集器
檢視:jinfo -flag UseParallelOldGC pid
並行收集器
響應時間優先
CMS:XX:+UseConcMarkSweepGC -XX:+UseParNewGC
G1:-XX:+UseG1GC
檢視:jinfo -flag UseConcMarkSweepGC pid
垃圾收集器搭配
可以從老年代記起:SerialOld最厲害,可以搭配所有的新生代收集器;
ParallelOld只能搭配Parallel新生代的,也就是Parallel Scavenge;
CMS 只能搭配Serial和ParNew;
最後G1只能自己和自己玩。
如何選擇垃圾收集器
優先調整堆的大小讓伺服器自己來選擇
如果記憶體小於100M,使用序列收集器
如果是單核,並且沒有時間停頓的要求,序列或者讓jvm自己選
如果允許停頓時間操作1秒,選擇並行或者讓jvm自己選
如果響應時間最重要,並且不能操作1秒,使用併發收集器
GC調優指南:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/toc.html
如何選擇垃圾收集器:
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/collectors.html
Parallel Collector
-XX:+UseParallelGC 手動開啟,Server預設開啟
-XX:ParallelGCThreads= 多少個GC執行緒
CPU>8 N=5/8
CPU< 8 N=CPU
-XX:MaxGCPauseMillis=
-XX:GCTimeRatio=
-Xmx
動態記憶體調整
-XX:YoungGenerationSizeIncrement=也就是每次gc之後發現之前的記憶體不夠,那麼就會動態增加記憶體
-XX:TenuredGenerationSizeIncrement=
-XX:AdaptiveSizeDecrementScaleFactor=
CMS Collector
特點
併發收集
低停頓 低延時
老年代收集器
CMS垃圾收集過程
1、CMS initial mark:初始標記root,STW
2、CMS concurrent mark:併發標記
3、CMS-concurrent-preclean:併發預清理
4、CMS remark:重新標記,STW
5、CMS concurrent sweep:併發清除
6、CMS-concurrent-reset:併發重置
CMS的特點
CPU敏感
浮動垃圾
空間碎片
CMS的相關引數
-XX:ConcGCThreads:併發的GC執行緒數
-XX:+UseCMSCompactAtFullCollection:FullGC之後做壓縮
-XX:CMSFullGCsBeforeCompaction:多少次FullGC之後壓縮一次
-XX:CMSInitiatingOccupancyFraction:觸發FullGC
-XX:+UseCMSInitiatingOccupancyOnly:是否動態調整
-XX:+CMSScavengeBeforeRemark:FullGC之前先做YGC
-XX:+CMSClassUnloadingEnabled:啟用回收Perm區
iCMS
使用於單核或者雙核
G1 Collector
G1垃圾收集器介紹:https://zhuanlan.zhihu.com/p/22591838
簡介:
The first focus of G1 is to provide a solution for users running applications that require large heaps with limited GC latency.This means heap sizes of around 6GB or larger,and a stable and predictable pause time below 0.5 senconds.
G1屬於新生代和老生代收集器
G1的幾個概念
Region
SATB:Snapshot-At-The-Beginning,它是通過Root Tracing得到的,GC開始時候存活物件的快照。
RSet:記錄了其他Region中的物件引用本Region中物件的關係,屬於points-into結構(誰引用了我的物件)
YoungGC
新物件進入Eden區
存活物件拷貝到Survivor區
存活時間達到年齡閾值是,物件晉升到Old區
MixedGC
不是FullGC,回收所有的Young和部分Old
global concurrent marking
global concurrent marking
1、initial marking phase:標記GC Root,STW
2、Root region scanning phase:標記存活Region
3、Concurrent marking phase:標記存活的物件
4、Remark phase:重新標記,STW
5、Cleanup phase:部分STW
MixedGC時機
initiationHeapOccupancyPercent:堆佔有率達到這個數值則觸發global concurrent marking,預設45%
G1HeapWastePercent:在global concurrent marking結束之後,可以知道區有多少空間要被回收,在每次YGC之後和再次發生Mixed GC之前,會檢查垃圾佔比是否達到此引數,只有達到了,下次才會發生Mixed GC
MixedGC相關引數
G1MixedGCLiveThresholdPercent:Old 區的region被回收時候的存活物件佔比
G1MixedGCCountTarget:
一次global concurrent marking之後,最多執行Mixed GC的次數
G1OldCSetRegionThresholdPercent:一次Mixed GC中能被選入CSet的最多old區的region數量
常用引數
-XX:+UseG1GC 開啟G1
-XX:G1HeapRegionSize=n,region的大小,1~32M,2048個
-XX:MaxGCPauseMilles=200最大停頓時間
-XX:G1NewSizePercent,-XX:G1MaxNewSizePercent
-XX:G1ReservePercent=10, 保留防止to space溢位
-XX:ParallelGCThreads=n STW執行緒數
-XX:ConcGCThreads=n 併發執行緒數=1/4*併發
最佳實踐
年輕代大小
避免使用-Xmn、-XX:NewRatio等顯示設定Young區大小,會覆蓋停頓時間目標
暫停時間目標
暫停時間不要太苛刻,其吞吐量目標是90%的應用程式的時間和10%的垃圾回收時間,太苛刻會直接影響到吞吐量
關於MixedGC調優
-XX:InitiatingHeapOccupancyPercent
-XX:G1MixedGCLiveThresholdPercent、-XX:G1HeapWastePercent
-XX:G1MixedGCCountTarget
-XX:G1OldCSetRegionThresholdPercent
MixedGC調優推薦:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/g1_gc_tuning.html#recommendations
G1垃圾收集器介紹:https://zhuanlan.zhihu.com/p/22591838
是否需要切換到G1
50%以上的堆被存活物件佔用
物件分配和晉升的速度變化非常大
垃圾回收的時間特別長、超過了一秒
視覺化gc日誌分析工具
列印日誌相關引數
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps
-XX:+PrintGCDateStamps
-Xloggc:$CATALINA_HOME/logs/gc.log :列印日誌的位置
-XX:+PrintHeapAtGC
-XX:+PrintTenuringDistribution
cms日誌
https://blogs.oracle.com/poonam/understanding-cms-gc-logs
g1日誌
https://blogs.oracle.com/poonam/understanding-g1-gc-logs
線上工具:http://gceasy.io/
GCViewer
https://github.com/chewiebug/GCViewer
直接啟動java -jar 啟動就可以了
主要關注吞吐量和停頓時間這些值
Tomcat的gc調優實戰
調優步驟:
- 列印gc日誌
- 根據gc日誌得到關鍵性指標
- 分析gc原因,調優jvm引數
初始化引數:
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps
-XX:+PrintGCDateStamps -XX:+DisableExplicitGC
-Xloggc:$CATALINA_HOME/logs/gc.log :列印日誌的位置
-XX:+PrintHeapAtGC
-XX:+PrintTenuringDistribution
關於DisableExplicitGC這個,有必要了解一下,如果啟動了這個引數,不能顯示呼叫gc,System.gc()這個方法就沒用了,那麼在某一些使用到堆外記憶體的框架如netty中,就會存在記憶體洩漏的風險。
參考:https://blog.csdn.net/aitangyong/article/details/39403031
Parallel collector調優
parallel gc調優指南:
- 除非確定,否則不要設定最大堆記憶體
- 優先設定吞吐量目標
- 如果吞吐量目標達不到,調大最大記憶體,不能讓os使用swap,如果仍然達不到,降低目標。
- 吞吐量能達到,GC時間太長、設定停頓時間的目標。
在官方文件的這個地方:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/ergonomics.html#sthref15
介紹了parallell gc調優的兩種方式:
1、設定最大停頓時間XX:MaxGCPauseMillis
2、設定吞吐量XX:GCTimeRatio
這兩個引數只適用於paraller collector
而在介紹Parallel collector的的文章這裡:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/parallel.html#CHDCFBIF
說了另外兩種方式:
3、設定記憶體動態變化:XX:YoungGenerationSizeIncrement、XX:TenuredGenerationSizeIncrement、XX:AdaptiveSizeDecrementScaleFactor,每次gc之後,都會看gc前後的記憶體,從而增加或者減少記憶體
4、通過檢視gc的情況,設定記憶體大小,可以設定Xms(initial heap size) 、Xmx(maximum heap size)和Xmn(新生代的大小)還有MetaspaceSize和MaxMetaspaceSize
CMS GC調優
由於jdk1.8之後更加推薦使用G1,所以cms的調優自己看官網:
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/cms.html#concurrent_mark_sweep_cms_collector
G1 GC調優
g1gc調優指南:
年輕代大小
避免使用-Xmn、-XX:NewRatio等顯示設定Young區大小,會覆蓋停頓時間目標
暫停時間目標
暫停時間不要太苛刻,其吞吐量目標是90%的應用程式的時間和10%的垃圾回收時間,太苛刻會直接影響到吞吐量
關於MixedGC調優
-XX:InitiatingHeapOccupancyPercent
-XX:G1MixedGCLiveThresholdPercent、-XX:G1HeapWastePercent
-XX:G1MixedGCCountTarget
-XX:G1OldCSetRegionThresholdPercent
其實這裡的介紹就是官網的介紹
官網:
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/g1_gc_tuning.html#recommendations
這裡介紹了調優的三種方式:
1、避免設定固定的記憶體例如Xms(initial heap size) 、Xmx(maximum heap size)和Xmn(新生代的大小)還有MetaspaceSize和MaxMetaspaceSize,但是可以必要條件下可以調這些引數
2、設定最大停頓時間例如;MaxGCPauseMillis=100
3、對於mixed gc引數的設定,如:
-XX:InitiatingHeapOccupancyPercent、
-XX:G1MixedGCLiveThresholdPercent、
-XX:G1HeapWastePercent、
-XX:G1MixedGCCountTarget 、
-XX:G1OldCSetRegionThresholdPercent
ps:最後思考一點,fullgc和young gc是相對parallel gc,而mixed是相對g1gc的嗎?