何時(shí)進(jìn)行JVM調(diào)優(yōu)
- Heap內(nèi)存(老年代)持續(xù)上漲達(dá)到設(shè)置的最大內(nèi)存值邀摆;
- Full GC 次數(shù)頻繁;
- GC 停頓時(shí)間過長(超過1秒);
- 應(yīng)用出現(xiàn)OutOfMemory 等內(nèi)存異常;
- 應(yīng)用中有使用本地緩存且占用大量內(nèi)存空間;
- 系統(tǒng)吞吐量與響應(yīng)性能不高或下降击蹲。
JVM調(diào)優(yōu)目標(biāo)
調(diào)優(yōu)的最終目的都是為了令應(yīng)用程序使用最小的硬件消耗來承載更大的吞吐。jvm調(diào)優(yōu)主要是針對(duì)垃圾收集器的收集性能優(yōu)化婉宰,令運(yùn)行在虛擬機(jī)上的應(yīng)用能夠使用更少的內(nèi)存以及延遲獲取更大的吞吐量歌豺。
- 延遲:GC低停頓和GC低頻率;
- 低內(nèi)存占用心包;
- 高吞吐量;
吞吐量:重要指標(biāo)之一类咧,是指不考慮垃圾收集引起的停頓時(shí)間或內(nèi)存消耗,垃圾收集器能支撐應(yīng)用達(dá)到的最高性能指標(biāo)。
延遲:其度量標(biāo)準(zhǔn)是縮短由于垃圾啊收集引起的停頓時(shí)間或者完全消除因垃圾收集所引起的停頓轮听,避免應(yīng)用運(yùn)行時(shí)發(fā)生抖動(dòng)骗露。
內(nèi)存占用:垃圾收集器流暢運(yùn)行所需要 的內(nèi)存數(shù)量。
其中血巍,任何一個(gè)屬性性能的提高萧锉,幾乎都是以犧牲其他屬性性能的損為代價(jià)的,不可兼得述寡。具體根據(jù)在業(yè)務(wù)中的重要性確定柿隙。
JVM調(diào)優(yōu)量化目標(biāo)
- Heap 內(nèi)存使用率 <= 70%;
- Old generation內(nèi)存使用率<= 70%;
- avgpause <= 1秒;
- Full gc 次數(shù)0 或 avg pause interval >= 24小時(shí) ;
JVM調(diào)優(yōu)的步驟
一般情況下,JVM調(diào)優(yōu)可通過以下步驟進(jìn)行:
- 分析GC日志及dump文件鲫凶,判斷是否需要優(yōu)化禀崖,確定瓶頸問題點(diǎn);
- 確定JVM調(diào)優(yōu)量化目標(biāo)螟炫;
- 確定JVM調(diào)優(yōu)參數(shù)(根據(jù)歷史JVM參數(shù)來調(diào)整)波附;
- 依次調(diào)優(yōu)內(nèi)存、延遲昼钻、吞吐量等指標(biāo)掸屡;
- 對(duì)比觀察調(diào)優(yōu)前后的差異;
- 不斷的分析和調(diào)整然评,直到找到合適的JVM參數(shù)配置仅财;
- 找到最合適的參數(shù),將這些參數(shù)應(yīng)用到所有服務(wù)器碗淌,并進(jìn)行后續(xù)跟蹤盏求。
以上操作步驟中,某些步驟是需要多次不斷迭代完成的亿眠。一般是從滿足程序的內(nèi)存使用需求開始的碎罚,之后是時(shí)間延遲的要求,最后才是吞吐量的要求缕探,要基于這個(gè)步驟來不斷優(yōu)化魂莫,每一個(gè)步驟都是進(jìn)行下一步的基礎(chǔ)还蹲,不可逆行之爹耗。
JVM參數(shù)
-XX 參數(shù)被稱為不穩(wěn)定參數(shù),此類參數(shù)的設(shè)置很容易引起JVM 性能上的差異谜喊,使JVM存在極大的不穩(wěn)定性潭兽。如果此類參數(shù)設(shè)置合理將大大提高JVM的性能及穩(wěn)定性。
不穩(wěn)定參數(shù)語法規(guī)則包含以下內(nèi)容斗遏。
布爾類型參數(shù)值:
- -XX:+:'+'表示啟用該選項(xiàng)
- -XX:-:'-'表示關(guān)閉該選項(xiàng)
數(shù)字類型參數(shù)值:
- -XX:=
字符串類型參數(shù)值:
- -XX:=
JVM參數(shù)解析及調(diào)優(yōu)
比如以下參數(shù)示例:
-Xmx4g –Xms4g –Xmn1200m –Xss512k -XX:NewRatio=4 -XX:SurvivorRatio=8 -XX:PermSize=100m -XX:MaxPermSize=256m -XX:MaxTenuringThreshold=15
上面為Java7及以前版本的示例山卦,在Java8中永久代的參數(shù)-XX:PermSize和-XX:MaxPermSize已經(jīng)失效。這在前面章節(jié)中已經(jīng)講到诵次。
參數(shù)解析:
- -Xmx4g:堆內(nèi)存最大值為4GB账蓉。
- -Xms4g:初始化堆內(nèi)存大小為4GB枚碗。
- -Xmn1200m:設(shè)置年輕代大小為1200MB。增大年輕代后铸本,將會(huì)減小年老代大小肮雨。此值對(duì)系統(tǒng)性能影響較大,Sun官方推薦配置為整個(gè)堆的3/8箱玷。
- -Xss512k:設(shè)置每個(gè)線程的堆棧大小怨规。JDK5.0以后每個(gè)線程堆棧大小為1MB,以前每個(gè)線程堆棧大小為256K锡足。應(yīng)根據(jù)應(yīng)用線程所需內(nèi)存大小進(jìn)行調(diào)整波丰。在相同物理內(nèi)存下,減小這個(gè)值能生成更多的線程舶得。但是操作系統(tǒng)對(duì)一個(gè)進(jìn)程內(nèi)的線程數(shù)還是有限制的掰烟,不能無限生成,經(jīng)驗(yàn)值在3000~5000左右沐批。
- -XX:NewRatio=4:設(shè)置年輕代(包括Eden和兩個(gè)Survivor區(qū))與年老代的比值(除去持久代)媚赖。設(shè)置為4,則年輕代與年老代所占比值為1:4珠插,年輕代占整個(gè)堆棧的1/5
- -XX:SurvivorRatio=8:設(shè)置年輕代中Eden區(qū)與Survivor區(qū)的大小比值惧磺。設(shè)置為8,則兩個(gè)Survivor區(qū)與一個(gè)Eden區(qū)的比值為2:8捻撑,一個(gè)Survivor區(qū)占整個(gè)年輕代的1/10
- -XX:PermSize=100m:初始化永久代大小為100MB磨隘。
- -XX:MaxPermSize=256m:設(shè)置持久代大小為256MB。
- -XX:MaxTenuringThreshold=15:設(shè)置垃圾最大年齡顾患。如果設(shè)置為0的話番捂,則年輕代對(duì)象不經(jīng)過Survivor區(qū),直接進(jìn)入年老代江解。對(duì)于年老代比較多的應(yīng)用设预,可以提高效率。如果將此值設(shè)置為一個(gè)較大值犁河,則年輕代對(duì)象會(huì)在Survivor區(qū)進(jìn)行多次復(fù)制鳖枕,這樣可以增加對(duì)象再年輕代的存活時(shí)間,增加在年輕代即被回收的概論桨螺。
新生代宾符、老生代、永久代的參數(shù)灭翔,如果不進(jìn)行指定魏烫,虛擬機(jī)會(huì)自動(dòng)選擇合適的值,同時(shí)也會(huì)基于系統(tǒng)的開銷自動(dòng)調(diào)整。
可調(diào)優(yōu)參數(shù):
- -Xms:初始化堆內(nèi)存大小哄褒,默認(rèn)為物理內(nèi)存的1/64(小于1GB)稀蟋。
- -Xmx:堆內(nèi)存最大值。默認(rèn)(MaxHeapFreeRatio參數(shù)可以調(diào)整)空余堆內(nèi)存大于70%時(shí)呐赡,JVM會(huì)減少堆直到-Xms的最小限制糊治。
- -Xmn:新生代大小,包括Eden區(qū)與2個(gè)Survivor區(qū)罚舱。
- -XX:SurvivorRatio=1:Eden區(qū)與一個(gè)Survivor區(qū)比值為1:1井辜。
- -XX:MaxDirectMemorySize=1G:直接內(nèi)存。報(bào)java.lang.OutOfMemoryError: Direct buffer memory異彻苊疲可以上調(diào)這個(gè)值粥脚。
- -XX:+DisableExplicitGC:禁止運(yùn)行期顯式地調(diào)用System.gc()來觸發(fā)fulll GC。
- 注意: Java RMI的定時(shí)GC觸發(fā)機(jī)制可通過配置-Dsun.rmi.dgc.server.gcInterval=86400來控制觸發(fā)的時(shí)間包个。
- -XX:CMSInitiatingOccupancyFraction=60:老年代內(nèi)存回收閾值刷允,默認(rèn)值為68。
- -XX:ConcGCThreads=4:CMS垃圾回收器并行線程線碧囊,推薦值為CPU核心數(shù)树灶。
- -XX:ParallelGCThreads=8:新生代并行收集器的線程數(shù)。
- -XX:MaxTenuringThreshold=10:設(shè)置垃圾最大年齡糯而。如果設(shè)置為0的話天通,則年輕代對(duì)象不經(jīng)過Survivor區(qū),直接進(jìn)入年老代熄驼。對(duì)于年老代比較多的應(yīng)用像寒,可以提高效率。如果將此值設(shè)置為一個(gè)較大值瓜贾,則年輕代對(duì)象會(huì)在Survivor區(qū)進(jìn)行多次復(fù)制诺祸,這樣可以增加對(duì)象再年輕代的存活時(shí)間,增加在年輕代即被回收的概論祭芦。
- -XX:CMSFullGCsBeforeCompaction=4:指定進(jìn)行多少次fullGC之后筷笨,進(jìn)行tenured區(qū) 內(nèi)存空間壓縮。
- -XX:CMSMaxAbortablePrecleanTime=500:當(dāng)abortable-preclean預(yù)清理階段執(zhí)行達(dá)到這個(gè)時(shí)間時(shí)就會(huì)結(jié)束龟劲。
- 在設(shè)置的時(shí)候胃夏,如果關(guān)注性能開銷的話,應(yīng)盡量把永久代的初始值與最大值設(shè)置為同一值咸灿,因?yàn)橛谰么拇笮≌{(diào)整需要進(jìn)行FullGC才能實(shí)現(xiàn)构订。
內(nèi)存優(yōu)化示例
當(dāng)JVM運(yùn)行穩(wěn)定之后侮叮,觸發(fā)了FullGC我們一般會(huì)拿到如下信息:
以上gc日志中避矢,在發(fā)生fullGC之時(shí),整個(gè)應(yīng)用的堆占用以及GC時(shí)間。為了更加精確需多次收集审胸,計(jì)算平均值亥宿。或者是采用耗時(shí)最長的一次FullGC來進(jìn)行估算砂沛。上圖中烫扼,老年代空間占用在93168kb(約93MB),以此定為老年代空間的活躍數(shù)據(jù)碍庵。則其他堆空間的分配映企,基于以下規(guī)則來進(jìn)行。
- java heap:參數(shù)-Xms和-Xmx静浴,建議擴(kuò)大至3-4倍FullGC后的老年代空間占用堰氓。
- 永久代:-XX:PermSize和-XX:MaxPermSize,建議擴(kuò)大至1.2-1.5倍FullGc后的永久帶空間占用苹享。
- 新生代:-Xmn双絮,建議擴(kuò)大至1-1.5倍FullGC之后的老年代空間占用。
- 老年代:2-3倍FullGC后的老年代空間占用得问。
基于以上規(guī)則囤攀,則對(duì)參數(shù)定義如下:
java -Xms373m -Xmx373m -Xmn140m -XX:PermSize=5m -XX:MaxPermSize=5m
延遲優(yōu)化示例
對(duì)延遲性優(yōu)化,首先需要了解延遲性需求及可調(diào)優(yōu)的指標(biāo)有哪些宫纬。
- 應(yīng)用程序可接受的平均停滯時(shí)間: 此時(shí)間與測(cè)量的Minor
- GC持續(xù)時(shí)間進(jìn)行比較焚挠。可接受的Minor GC頻率:Minor
- GC的頻率與可容忍的值進(jìn)行比較漓骚。
- 可接受的最大停頓時(shí)間:最大停頓時(shí)間與最差情況下FullGC的持續(xù)時(shí)間進(jìn)行比較宣蔚。
- 可接受的最大停頓發(fā)生的頻率:基本就是FullGC的頻率。
其中认境,平均停滯時(shí)間和最大停頓時(shí)間胚委,對(duì)用戶體驗(yàn)最為重要。對(duì)于上面的指標(biāo)叉信,相關(guān)數(shù)據(jù)采集包括:MinorGC的持續(xù)時(shí)間亩冬、統(tǒng)計(jì)MinorGC的次數(shù)、FullGC的最差持續(xù)時(shí)間硼身、最差情況下硅急,F(xiàn)ullGC的頻率稻励。
如上圖泻仙,Minor GC的平均持續(xù)時(shí)間0.069秒,MinorGC的頻率為0.389秒一次关霸。
新生代空間越大丑罪,Minor GC的GC時(shí)間越長荚板,頻率越低凤壁。如果想減少其持續(xù)時(shí)長,就需要減少其空間大小跪另。如果想減小其頻率拧抖,就需要加大其空間大小。
這里以減少了新生代空間10%的大小免绿,來減小延遲時(shí)間唧席。在此過程中,應(yīng)該保持老年代和持代的大小不變化嘲驾。調(diào)優(yōu)后的參數(shù)如下變化:
java -Xms359m -Xmx359m -Xmn126m -XX:PermSize=5m -XX:MaxPermSize=5m
吞吐量調(diào)優(yōu)
吞吐量調(diào)優(yōu)主要是基于應(yīng)用程序的吞吐量要求而來的淌哟,應(yīng)用程序應(yīng)該有一個(gè)綜合的吞吐指標(biāo),這個(gè)指標(biāo)基于整個(gè)應(yīng)用的需求和測(cè)試而衍生出來的辽故。
評(píng)估當(dāng)前吞吐量和目標(biāo)差距是否巨大绞绒,如果在20%左右,可以修改參數(shù)榕暇,加大內(nèi)存蓬衡,再次從頭調(diào)試,如果巨大就需要從整個(gè)應(yīng)用層面來考慮彤枢,設(shè)計(jì)以及目標(biāo)是否一致了狰晚,重新評(píng)估吞吐目標(biāo)。
對(duì)于垃圾收集器來說缴啡,提升吞吐量的性能調(diào)優(yōu)的目標(biāo)就是盡可能避免或者很少發(fā)生FullGC或者Stop-The-World壓縮式垃圾收集(CMS)壁晒,因?yàn)檫@兩種方式都會(huì)造成應(yīng)用程序吞吐降低。盡量在MinorGC 階段回收更多的對(duì)象业栅,避免對(duì)象提升過快到老年代秒咐。
調(diào)優(yōu)工具
借助GCViewer日志分析工具,可以非常直觀地分析出待調(diào)優(yōu)點(diǎn)碘裕⌒。可從以下幾方面來分析:
Memory,分析Totalheap、Tenuredheap帮孔、Youngheap內(nèi)存占用率及其他指標(biāo)雷滋,理論上內(nèi)存占用率越小越好;
Pause文兢,分析Gc pause晤斩、Fullgc pause、Total pause三個(gè)大項(xiàng)中各指標(biāo)姆坚,理論上GC次數(shù)越少越好澳泵,GC時(shí)長越小越好;