大型跨境電商 JVM 調優經歷

前提:java

某大型跨境電商業務發展很是快,線上機器擴容也很頻繁,可是對於線上機器的運行狀況,特別是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)公司的推薦配置:
img
配置說明:算法

  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:設置持久代大小sql

  2. 收集器設置
    o -XX:+UseSerialGC:設置串行收集器
    o -XX:+UseParallelGC:設置並行收集器
    o -XX:+UseParalledlOldGC:設置並行年老代收集器
    o -XX:+UseConcMarkSweepGC:設置併發收集器express

  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天一次:
    img
    online(另一個工程):能夠明顯看到fgc的統計頻率少了不少;
    img
    第二個變化:fgc的時間減小
    img
    img
    原來一次fgc要將近500ms,如今只要100ms不到了。
    也證實了cms最大的好處就是減小fgc的停頓時間。
    二:壓測及大促表現
    fgc的時間基本上是大大縮短,yanggc的時間變長,次數變化不大;
    數據來源:測試團隊的壓測總結多線程

xxxx-online4.server.org CMS xxxx-online1.server.org CMS xxxx-online34.server.org 默認垃圾收集器 說明  
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時間比默認垃圾收集器最差狀況要差

三:關於哨兵上統計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的時間過長
      以下圖:
      img
      能夠採用的方式是:
      在CMS GC前啓動一次ygc,目的在於減小old gen對ygc gen的引用,下降remark時的開銷—–通常CMS的GC耗時 80%都在remark階段
      -XX:+CMSScavengeBeforeRemark
      jmap分析:
      img問題四: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慢)。
相關文章
相關標籤/搜索