大型跨境电商 JVM 调优经历
大型跨境電商 JVM 調(diào)優(yōu)經(jīng)歷
前提:
某大型跨境電商業(yè)務(wù)發(fā)展非常快,線上機器擴(kuò)容也很頻繁,但是對于線上機器的運行情況,特別是jvm內(nèi)存的情況,一直沒有一個統(tǒng)一的標(biāo)準(zhǔn)來給到各個應(yīng)用服務(wù)的owner。經(jīng)過618大促之后,和運維的同學(xué)討論了下,希望將線上服務(wù)器的jvm參數(shù)標(biāo)準(zhǔn)化,可以以一個統(tǒng)一的方式給到各個應(yīng)用,提升線上服務(wù)器的穩(wěn)定性,同時減少大家都去調(diào)整jvm參數(shù)的時間。
參考了之前在淘寶天貓工作的公司的經(jīng)歷:經(jīng)過大家討論,根據(jù)jdk的版本以及線上機器配置,確定了一個推薦的默認(rèn)jvm模版:
最終推薦的jvm模版:
jdk版本 機器配置 建議jvm參數(shù) 備注
jdk1.7 6V8G -server -Xms4g -Xmx4g -Xmn2g -Xss768k -XX:PermSize=512m -XX:MaxPermSize=512m
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSClassUnloadingEnabled
-XX:+DisableExplicitGC -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=68
-verbose:gc -XX:+PrintGCDetails -Xloggc:{CATALINA_BASE}/logs/gc.log -XX:+PrintGCDateStamps
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath={CATALINA_BASE}/logs 前臺
jdk1.7 8V8G -server -Xms4g -Xmx4g -Xmn2g -Xss768k -XX:PermSize=512m -XX:MaxPermSize=512m
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSClassUnloadingEnabled
-XX:+DisableExplicitGC -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=68
-verbose:gc -XX:+PrintGCDetails -Xloggc:{CATALINA_BASE}/logs/gc.log -XX:+PrintGCDateStamps
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath={CATALINA_BASE}/logs 前臺
jdk1.7 4V8G -server -Xms4g -Xmx4g -Xmn2g -Xss768k -XX:PermSize=512m -XX:MaxPermSize=512m
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSClassUnloadingEnabled
-XX:+DisableExplicitGC -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=68
-verbose:gc -XX:+PrintGCDetails -Xloggc:{CATALINA_BASE}/logs/gc.log -XX:+PrintGCDateStamps
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath={CATALINA_BASE}/logs 前臺
jdk1.7 6V8G -server -Xms4g -Xmx4g -XX:MaxPermSize=512m
-verbose:gc -XX:+PrintGCDetails -Xloggc{CATALINA_BASE}/logs/gc.log -XX:+PrintGCTimeStamps \ 后臺
某互聯(lián)網(wǎng)(bat)公司的推薦配置:
配置說明:
堆設(shè)置
-Xms:初始堆大小
-Xmx:最大堆大小
-XX:NewSize=n:設(shè)置年輕代大小
-XX:NewRatio=n:設(shè)置年輕代和年老代的比值。如:為3,表示年輕代與年老代比值為1:3,年輕代占整個年輕代年老代和的1/4
-XX:SurvivorRatio=n:年輕代中Eden區(qū)與兩個Survivor區(qū)的比值。注意Survivor區(qū)有兩個。如:3,表示Eden:Survivor=3:2,一個Survivor區(qū)占整個年輕代的1/5
-XX:MaxPermSize=n:設(shè)置持久代大小
收集器設(shè)置
-XX:+UseSerialGC:設(shè)置串行收集器
-XX:+UseParallelGC:設(shè)置并行收集器
-XX:+UseParalledlOldGC:設(shè)置并行年老代收集器
-XX:+UseConcMarkSweepGC:設(shè)置并發(fā)收集器
垃圾回收統(tǒng)計信息
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename
并行收集器設(shè)置
-XX:ParallelGCThreads=n:設(shè)置并行收集器收集時使用的CPU數(shù)。并行收集線程數(shù)。
-XX:MaxGCPauseMillis=n:設(shè)置并行收集最大暫停時間
-XX:GCTimeRatio=n:設(shè)置垃圾回收時間占程序運行時間的百分比。公式為1/(1+n)
并發(fā)收集器設(shè)置
-XX:+CMSIncrementalMode:設(shè)置為增量模式。適用于單CPU情況。
-XX:ParallelGCThreads=n:設(shè)置并發(fā)收集器年輕代收集方式為并行收集時,使用的CPU數(shù)。并行收集線程數(shù)。
參數(shù)解釋:
-Xms3072m -Xmx3072m
針對JVM堆的設(shè)置,通過-Xms -Xmx限定其最小、最大值
-Xmn1024m設(shè)置年輕代大小為1024m
整個JVM內(nèi)存大小=年輕代大小 + 年老代大小 + 持久代大小(perm)。
-Xss768k 設(shè)置每個線程的堆棧大小。JDK5.0以后每個線程堆棧大小為1M,以前每個線程堆棧大小為256K。更具應(yīng)用的線程所需內(nèi)存大小進(jìn)行調(diào)整。在相同物理內(nèi)存下,減小這個值能生成更多的線程。但是操作系統(tǒng)對一個進(jìn)程內(nèi)的線程數(shù)還是有限制的,不能無限生成,經(jīng)驗值在3000~5000左右。
-XX:PermSize=512m -XX:MaxPermSize=512m
持久代一般固定大小為64m,所以增大年輕代后,將會減小年老代大小。此值對系統(tǒng)性能影響較大,Sun官方推薦配置為整個堆的3/8。
設(shè)置非堆內(nèi)存初始值,默認(rèn)是物理內(nèi)存的1/64;由XX:MaxPermSize設(shè)置最大非堆內(nèi)存的大小,默認(rèn)是物理內(nèi)存的1/4
-XX:+UseConcMarkSweepGC
CMS收集器也被稱為短暫停頓并發(fā)收集器。它是對年老代進(jìn)行垃圾收集的。CMS收集器通過多線程并發(fā)進(jìn)行垃圾回收,盡量減少垃圾收集造成的停頓。CMS收集器對年輕代進(jìn)行垃圾回收使用的算法和Parallel收集器一樣。這個垃圾收集器適用于不能忍受長時間停頓要求快速響應(yīng)的應(yīng)用。
-XX:+UseParNewGC對年輕代采用多線程并行回收,這樣收得快;
-XX:+CMSClassUnloadingEnabled
如果你啟用了CMSClassUnloadingEnabled ,垃圾回收會清理持久代,移除不再使用的classes。這個參數(shù)只有在 UseConcMarkSweepGC 也啟用的情況下才有用。
-XX:+DisableExplicitGC禁止System.gc(),免得程序員誤調(diào)用gc方法影響性能;
-XX:+UseCMSInitiatingOccupancyOnly
標(biāo)志來命令JVM不基于運行時收集的數(shù)據(jù)來啟動CMS垃圾收集周期。而是,當(dāng)該標(biāo)志被開啟時,JVM通過CMSInitiatingOccupancyFraction的值進(jìn)行每一次CMS收集,而不僅僅是第一次。然而,請記住大多數(shù)情況下,JVM比我們自己能作出更好的垃圾收集決策。因此,只有當(dāng)我們充足的理由(比如測試)并且對應(yīng)用程序產(chǎn)生的對象的生命周期有深刻的認(rèn)知時,才應(yīng)該使用該標(biāo)志。
-XX:CMSInitiatingOccupancyFraction=68
默認(rèn)CMS是在tenured generation(年老代)占滿68%的時候開始進(jìn)行CMS收集,如果你的年老代增長不是那么快,并且希望降低CMS次數(shù)的話,可以適當(dāng)調(diào)高此值;
-XX:+UseParNewGC:對年輕代采用多線程并行回收,這樣收得快;
-XX:HeapDumpPath
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:/usr/aaa/dump/heap_trace.txt
上面的的參數(shù)打Heap Dump信息
-XX:+HeapDumpOnOutOfMemoryError
此參數(shù)可以控制OutOfMemoryError時打印堆的信息
大家可能注意到了,這里推薦采用cms方式進(jìn)行垃圾回收;
CMS是一種以獲取最短回收停頓時間為目標(biāo)的收集器,可以有效減少服務(wù)器停頓的時間;
CMS的GC線程對CPU的占用率會比較高,但在多核的服務(wù)器上還是展現(xiàn)了優(yōu)越的特性,目前也被部署在國內(nèi)的各大電商網(wǎng)站上。所以這里強烈推薦!
cms的概念:
CMS收集器也被稱為短暫停頓并發(fā)收集器。它是對年老代進(jìn)行垃圾收集的。CMS收集器通過多線程并發(fā)進(jìn)行垃圾回收,盡量減少垃圾收集造成的停頓。CMS收集器對年輕代進(jìn)行垃圾回收使用的算法和Parallel收集器一樣。這個垃圾收集器適用于不能忍受長時間停頓要求快速響應(yīng)的應(yīng)用。CMS采用了多種方式盡可能降低GC的暫停時間,減少用戶程序停頓。停頓時間降低的同時犧牲了CPU吞吐量 。這是在停頓時間和性能間做出的取舍,可以簡單理解為"空間(性能)"換時間。
調(diào)整的節(jié)奏:
由于怕影響線上應(yīng)用,所以調(diào)整的步驟分三步:
第一步:部分影響少量機器試點,對比未調(diào)整的機器,觀察調(diào)整后的結(jié)果;
第二步:調(diào)整部分應(yīng)用的參數(shù),進(jìn)行壓測,觀察高并發(fā)壓測之后的效果;
第三步:調(diào)整部分核心應(yīng)用的jvm參數(shù),通過818大促來實際檢驗效果;
目前818大促已經(jīng)結(jié)果。正好做一個個總結(jié)。
一. 長期表現(xiàn)
第一個變化:fgc的次數(shù)減少,減少了大概一倍以上;
mobile工程,調(diào)整前基本上一天1-2輛次,調(diào)整后基本上就是2-3天一次:
online(另外一個工程):可以明顯看到fgc的統(tǒng)計頻率少了很多;
第二個變化:fgc的時間減少
原來一次fgc要將近500ms,現(xiàn)在只要100ms不到了。
也證明了cms最大的好處就是減少fgc的停頓時間。
二. 壓測及大促表現(xiàn)
fgc的時間基本上是大大縮短,yanggc的時間變長,次數(shù)變化不大;
數(shù)據(jù)來源:測試團(tuán)隊的壓測總結(jié)
| fullgc次數(shù) | 1 | 1 | 1 | |
| fullgc總時間 | 343 | 250 | 1219 | |
| 默認(rèn)垃圾收集器/CMS fullgc 時間 | 3.55 | 4.88 | CMS fullgc時間比默認(rèn)垃圾收集器時間明顯要少。 | |
| fullgc時間點 | 2:48:36 | 3:14:36 | 5:30:36 | |
| fullgc時使用率CPU% | 40% | 10% | 16% | |
| fullgc時的load Average | 1.19 | 0.49 | 1.21 | |
| younggc總次數(shù) | 1094 | 1098 | 1078 | |
| younggc總時間 | 44093 | 44632 | 30387 | |
| younggc平均時間 | 40.30 | 40.65 | 28.19 | |
| younggc最大時間 | 1332 | 1268 | 928 | |
| CMS/默認(rèn)垃圾收集器(younggc總時間) | 1.45 | 1.47 | CMS younggc時間比默認(rèn)垃圾收集器耗時 | |
| CMS/默認(rèn)垃圾收集器(younggc平均時間) | 1.43 | 1.44 | CMS younggc時間比默認(rèn)垃圾收集器耗時 | |
| CMS/默認(rèn)垃圾收集器(younggc最大時間) | 1.44 | 1.37 | CMS younggc時間比默認(rèn)垃圾收集器最差情況要差 |
三. 關(guān)于哨兵上統(tǒng)計full gc的次數(shù)的解釋
哨兵上我們可以安全的說:
Full GC的次數(shù)說的是stop the world的次數(shù),所以一次CMS至少會讓Full GC的次數(shù)+2,因為CMS Initial mark和remark都會stop the world,記做2次。而CMS可能失敗再引發(fā)一次Full GC
如果CMS并發(fā)GC過程中出現(xiàn)了concurrent mode failure的話那么接下來就會做一次mark-sweep-compact的full GC,這個是完全stop-the-world的。
正是這個特征,使得CMS的每個并發(fā)GC周期總共會更新full GC計數(shù)器兩次,initial mark與final re-mark各一次;如果出現(xiàn)concurrent mode failure,則接下來的full GC自己算一次。
四. 遇到的幾個問題
問題一:堆棧溢出;
-Xss256k這個參數(shù)調(diào)整了,遠(yuǎn)濤反饋可能會影響trace的調(diào)用。 報如下錯誤:
因為這個參數(shù)是設(shè)置每個線程的堆棧大小。JDK5.0以后每個線程堆棧大小為1M,以前每個線程堆棧大小為256K。在相同物理內(nèi)存下,減小這個值能生成更多的線程。
所以今天去掉某臺inventory機器的-Xss256k參數(shù),看一下是不是這個導(dǎo)致的
問題二:初始化標(biāo)記階段耗時過長:
一般的建議是cms階段兩次STW的時間不超過200ms,如果是CMS Initial mark階段導(dǎo)致的時間過長:
在初始化標(biāo)記階段(CMS Initial mark),為了最大限度地減少STW的時間開銷,我們可以使用:
-XX:+CMSParallelInitialMarkEnabled
開啟初始標(biāo)記過程中的并行化,進(jìn)一步提升初始化標(biāo)記效率;
問題三:remark階段stw的時間過長
如下圖:
可以采用的方式是:
在CMS GC前啟動一次ygc,目的在于減少old gen對ygc gen的引用,降低remark時的開銷-----一般CMS的GC耗時 80%都在remark階段
-XX:+CMSScavengeBeforeRemark
jmap分析:
問題四:nio框架占用DirectMemory導(dǎo)致的OutOfMemoryError
處理方式:使用XX:+DisableExplicitGC
增加DirectMemory的大小;
1、DirectMemory不屬于java堆內(nèi)存、分配內(nèi)存其實是調(diào)用操作系統(tǒng)的Os:malloc()函數(shù)。
2、容量可通過-XX:MaxDirectMemorySize指定,如果不指定,則默認(rèn)與Java堆的最大值(-Xmx指定)一樣。注意 ibm jvm默認(rèn)Direct Memory與-Xmx無直接關(guān)系。
3、Direct Memory 內(nèi)存的使用避免Java堆和Native堆中來回復(fù)制數(shù)據(jù)。從某些場景中提高性能。
4、直接ByteBuffer對象會自動清理本機緩沖區(qū),但這個過程只能作為Java堆GC的一部分來執(zhí)行,因此它們不會自動響應(yīng)施加在本機堆上的壓力。
5、GC僅在Java堆被填滿,以至于無法為堆分配請求提供服務(wù)時發(fā)生,或者在Java應(yīng)用程序中顯示調(diào)用System.gc()函數(shù)來釋放內(nèi)存(一些NIO框架就是用這個方法釋放占用的DirectMemory)。
6、該區(qū)域使用不合理,也是會引起OutOfMemoryError。
7、在需要頻繁創(chuàng)建Buffer的場合,由于創(chuàng)建和銷毀DirectBuffer的代價比較高昂,是不宜使用DirectBuffer的,但是如果能將DirectBuffer進(jìn)行復(fù)用,那么 ,在讀寫頻繁的情況下,它完全可以大幅改善性能。(對DirectBuffer的讀寫比普通Buffer快,但是對他的創(chuàng)建和銷毀比普通Buffer慢)。
總結(jié)
以上是生活随笔為你收集整理的大型跨境电商 JVM 调优经历的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Hive学习笔记 —— Hive的数据类
- 下一篇: 中缀表达式转换成后缀表达式