前提:
某大型跨境電商業務發展很是快,線上機器擴容也很頻繁,可是對於線上機器的運行狀況,特別是jvm內存的狀況,一直沒有一個統一的標準來給到各個應用服務的owner。通過618大促以後,和運維的同窗討論了下,但願將線上服務器的jvm參數標準化,能夠以一個統一的方式給到各個應用,提高線上服務器的穩定性,同時減小你們都去調整jvm參數的時間。
參考了以前在淘寶天貓工做的公司的經歷:通過你們討論,根據jdk的版本以及線上機器配置,肯定了一個推薦的默認jvm模版:
最終推薦的jvm模版:
jdk版本 機器配置 建議jvm參數 備註
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 \ 後臺
某互聯網(bat)公司的推薦配置:
配置說明:
1. 堆設置
o -Xms:初始堆大小
o -Xmx:最大堆大小
o -XX:NewSize=n:設置年輕代大小
o -XX:NewRatio=n:設置年輕代和年老代的比值。如:爲3,表示年輕代與年老代比值爲1:3,年輕代佔整個年輕代年老代和的1/4
o -XX:SurvivorRatio=n:年輕代中Eden區與兩個Survivor區的比值。注意Survivor區有兩個。如:3,表示Eden:Survivor=3:2,一個Survivor區佔整個年輕代的1/5
o -XX:MaxPermSize=n:設置持久代大小
2. 收集器設置
o -XX:+UseSerialGC:設置串行收集器
o -XX:+UseParallelGC:設置並行收集器
o -XX:+UseParalledlOldGC:設置並行年老代收集器
o -XX:+UseConcMarkSweepGC:設置併發收集器
3. 垃圾回收統計信息
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename
"
4. 並行收集器設置
-XX:ParallelGCThreads=n:設置並行收集器收集時使用的CPU數。並行收集線程數。
-XX:MaxGCPauseMillis=n:設置並行收集最大暫停時間
-XX:GCTimeRatio=n:設置垃圾回收時間佔程序運行時間的百分比。公式爲1/(1+n)
5. 併發收集器設置
-XX:+CMSIncrementalMode:設置爲增量模式。適用於單CPU狀況。
-XX:ParallelGCThreads=n:設置併發收集器年輕代收集方式爲並行收集時,使用的CPU數。並行收集線程數。
(4)
參數解釋:
-Xms3072m -Xmx3072m
針對JVM堆的設置,經過-Xms -Xmx限定其最小、最大值
-Xmn1024m設置年輕代大小爲1024m
整個JVM內存大小=年輕代大小 + 年老代大小 + 持久代大小(perm)。
-Xss768k 設置每一個線程的堆棧大小。JDK5.0之後每一個線程堆棧大小爲1M,之前每一個線程堆棧大小爲256K。更具應用的線程所需內存大小進行調整。在相同物理內存下,減少這個值能生成更多的線程。可是操做系統對一個進程內的線程數仍是有限制的,不能無限生成,經驗值在3000~5000左右。
-XX:PermSize=512m -XX:MaxPermSize=512m
持久代通常固定大小爲64m,因此增大年輕代後,將會減少年老代大小。此值對系統性能影響較大,Sun官方推薦配置爲整個堆的3/8。
設置非堆內存初始值,默認是物理內存的1/64;由XX:MaxPermSize設置最大非堆內存的大小,默認是物理內存的1/4
-XX:+UseConcMarkSweepGC
CMS收集器也被稱爲短暫停頓併發收集器。它是對年老代進行垃圾收集的。CMS收集器經過多線程併發進行垃圾回收,儘可能減小垃圾收集形成的停頓。CMS收集器對年輕代進行垃圾回收使用的算法和Parallel收集器同樣。這個垃圾收集器適用於不能忍受長時間停頓要求快速響應的應用。
-XX:+UseParNewGC對年輕代採用多線程並行回收,這樣收得快;
-XX:+CMSClassUnloadingEnabled
若是你啓用了CMSClassUnloadingEnabled ,垃圾回收會清理持久代,移除再也不使用的classes。這個參數只有在 UseConcMarkSweepGC 也啓用的狀況下才有用。
-XX:+DisableExplicitGC禁止System.gc(),省得程序員誤調用gc方法影響性能;
-XX:+UseCMSInitiatingOccupancyOnly
標誌來命令JVM不基於運行時收集的數據來啓動CMS垃圾收集週期。而是,當該標誌被開啓時,JVM經過CMSInitiatingOccupancyFraction的值進行每一次CMS收集,而不只僅是第一次。然而,請記住大多數狀況下,JVM比咱們本身能做出更好的垃圾收集決策。所以,只有當咱們充足的理由(好比測試)而且對應用程序產生的對象的生命週期有深入的認知時,才應該使用該標誌。
-XX:CMSInitiatingOccupancyFraction=68
默認CMS是在tenured generation(年老代)佔滿68%的時候開始進行CMS收集,若是你的年老代增加不是那麼快,而且但願下降CMS次數的話,能夠適當調高此值;
-XX:+UseParNewGC:對年輕代採用多線程並行回收,這樣收得快;
-XX:HeapDumpPath
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:/usr/aaa/dump/heap_trace.txt
上面的的參數打Heap Dump信息
" -XX:+HeapDumpOnOutOfMemoryError
此參數能夠控制OutOfMemoryError時打印堆的信息
你們可能注意到了,這裏推薦採用cms方式進行垃圾回收;
CMS是一種以獲取最短回收停頓時間爲目標的收集器,能夠有效減小服務器停頓的時間;
CMS的GC線程對CPU的佔用率會比較高,但在多核的服務器上仍是展示了優越的特性,目前也被部署在國內的各大電商網站上。因此這裏強烈推薦!
cms的概念:
CMS收集器也被稱爲短暫停頓併發收集器。它是對年老代進行垃圾收集的。CMS收集器經過多線程併發進行垃圾回收,儘可能減小垃圾收集形成的停頓。CMS收集器對年輕代進行垃圾回收使用的算法和Parallel收集器同樣。這個垃圾收集器適用於不能忍受長時間停頓要求快速響應的應用。CMS採用了多種方式儘量下降GC的暫停時間,減小用戶程序停頓。停頓時間下降的同時犧牲了CPU吞吐量 。這是在停頓時間和性能間作出的取捨,能夠簡單理解爲"空間(性能)"換時間。
調整的節奏:
因爲怕影響線上應用,因此調整的步驟分三步:
第一步:部分影響少許機器試點,對比未調整的機器,觀察調整後的結果;
第二步:調整部分應用的參數,進行壓測,觀察高併發壓測以後的效果;
第三步:調整部分核心應用的jvm參數,經過818大促來實際檢驗效果;
目前818大促已經結果。正好作一個個總結。
一:長期表現,
第一個變化:fgc的次數減小,減小了大概一倍以上;
mobile工程,調整前基本上一天1-2輛次,調整後基本上就是2-3天一次:
online(另一個工程):能夠明顯看到fgc的統計頻率少了不少;
第二個變化:fgc的時間減小
原來一次fgc要將近500ms,如今只要100ms不到了。
也證實了cms最大的好處就是減小fgc的停頓時間。
二:壓測及大促表現
fgc的時間基本上是大大縮短,yanggc的時間變長,次數變化不大;
數據來源:測試團隊的壓測總結
java
說明express |
||||
fullgc次數安全 |
1服務器 |
1多線程 |
1併發 |
|
fullgc總時間框架 |
343 |
250 |
1219 |
|
默認垃圾收集器/CMS fullgc 時間 |
3.55 |
4.88 |
CMS fullgc時間比默認垃圾收集器時間明顯要少。 |
|
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總次數 |
1094 |
1098 |
1078 |
|
younggc總時間 |
44093 |
44632 |
30387 |
|
younggc平均時間 |
40.30 |
40.65 |
28.19 |
|
younggc最大時間 |
1332 |
1268 |
928 |
|
CMS/默認垃圾收集器(younggc總時間) |
1.45 |
1.47 |
CMS younggc時間比默認垃圾收集器耗時 |
|
CMS/默認垃圾收集器(younggc平均時間) |
1.43 |
1.44 |
CMS younggc時間比默認垃圾收集器耗時 |
|
CMS/默認垃圾收集器(younggc最大時間) |
1.44 |
1.37 |
CMS younggc時間比默認垃圾收集器最差狀況要差 |
<!--EndFragment-->
三:關於哨兵上統計full gc的次數的解釋,哨兵上
咱們能夠安全的說:
1. Full GC == Major GC指的是對老年代/永久代的stop the world的GC
2. Full GC的次數 = 老年代GC時 stop the world的次數
3. Full GC的時間 = 老年代GC時 stop the world的總時間
4. CMS 不等於Full GC,咱們能夠看到CMS分爲多個階段,只有stop the world的階段被計算到了Full GC的次數和時間,而和業務線程併發的GC的次數和時間則不被認爲是Full GC
Full GC的次數說的是stop the world的次數,因此一次CMS至少會讓Full GC的次數+2,由於CMS Initial mark和remark都會stop the world,記作2次。而CMS可能失敗再引起一次Full GC
若是CMS併發GC過程當中出現了concurrent mode failure的話那麼接下來就會作一次mark-sweep-compact的full GC,這個是徹底stop-the-world的。
正是這個特徵,使得CMS的每一個併發GC週期總共會更新full GC計數器兩次,initial mark與final re-mark各一次;若是出現concurrent mode failure,則接下來的full GC本身算一次。
四:遇到的幾個問題:
問題一:堆棧溢出;
-Xss256k這個參數調整了,遠濤反饋可能會影響trace的調用。 報以下錯誤:
Java.lang.StackOverflowError
at net.sf.jsqlparser.util.deparser.ExpressionDeParser.visitBinaryExpression(ExpressionDeParser.java:278)
at net.sf.jsqlparser.util.deparser.ExpressionDeParser.visit(ExpressionDeParser.java:246)
at net.sf.jsqlparser.expression.operators.conditional.OrExpression.accept(OrExpression.java:37)
at net.sf.jsqlparser.util.deparser.ExpressionDeParser.visitBinaryExpression(ExpressionDeParser.java:278)
at net.sf.jsqlparser.util.deparser.ExpressionDeParser.visit(ExpressionDeParser.java:246)
由於這個參數是設置每一個線程的堆棧大小。JDK5.0之後每一個線程堆棧大小爲1M,之前每一個線程堆棧大小爲256K。在相同物理內存下,減少這個值能生成更多的線程。
因此今天去掉某臺inventory機器的-Xss256k參數,看一下是否是這個致使的
問題二:初始化標記階段耗時過長:
通常的建議是cms階段兩次STW的時間不超過200ms,若是是CMS Initial mark階段致使的時間過長:
在初始化標記階段(CMS Initial mark),爲了最大限度地減小STW的時間開銷,咱們可使用:
-XX:+CMSParallelInitialMarkEnabled
開啓初始標記過程當中的並行化,進一步提高初始化標記效率;
問題三:remark階段stw的時間過長
以下圖:
能夠採用的方式是:
在CMS GC前啓動一次ygc,目的在於減小old gen對ygc gen的引用,下降remark時的開銷-----通常CMS的GC耗時 80%都在remark階段
-XX:+CMSScavengeBeforeRemark
jmap分析:
問題四:nio框架佔用DirectMemory致使的OutOfMemoryError處理方式:使用XX:+DisableExplicitGC增長DirectMemory的大小;一、DirectMemory不屬於java堆內存、分配內存實際上是調用操做系統的Os:malloc()函數。二、容量可經過-XX:MaxDirectMemorySize指定,若是不指定,則默認與Java堆的最大值(-Xmx指定)同樣。注意 ibm jvm默認Direct Memory與-Xmx無直接關係。三、Direct Memory 內存的使用避免Java堆和Native堆中來回複製數據。從某些場景中提升性能。四、直接ByteBuffer對象會自動清理本機緩衝區,但這個過程只能做爲Java堆GC的一部分來執行,所以它們不會自動響應施加在本機堆上的壓力。五、GC僅在Java堆被填滿,以致於沒法爲堆分配請求提供服務時發生,或者在Java應用程序中顯示調用System.gc()函數來釋放內存(一些NIO框架就是用這個方法釋放佔用的DirectMemory)。六、該區域使用不合理,也是會引發OutOfMemoryError。七、在須要頻繁建立Buffer的場合,因爲建立和銷燬DirectBuffer的代價比較高昂,是不宜使用DirectBuffer的,可是若是能將DirectBuffer進行復用,那麼 ,在讀寫頻繁的狀況下,它徹底能夠大幅改善性能。(對DirectBuffer的讀寫比普通Buffer快,可是對他的建立和銷燬比普通Buffer慢)。