轉載佔小狼博客 應用:shark-新美大移動端網絡優化(每日接受移動端請求約150億)html
以上三個特色致使有大量小對象彙集在old區,高峯期old區域增加很是快,對象在一段時間內確定會消亡java
初始的線上gc的狀況以下android
對應的jvm參數爲bash
-Xms10g -Xmx10g -Xss512k -XX:PermSize=384m -XX:MaxPermSize=384m -XX:NewSize=7g -XX:MaxNewSize=7g -XX:SurvivorRatio=8 -XX:MaxDirectMemorySize=4g -XX:+UseParNewGC -XX:ParallelGCThreads=4 -XX:MaxTenuringThreshold=9 -XX:+UseConcMarkSweepGC -XX:+DisableExplicitGC -XX:+UseCMSInitiatingOccupancyOnly -XX:+ScavengeBeforeFullGC -XX:+UseCMSCompactAtFullCollection -XX:+CMSParallelRemarkEnabled -XX:CMSFullGCsBeforeCompaction=9 -XX:CMSInitiatingOccupancyFraction=70
能夠看到新生代爲7G(其中Survivor爲2*700M),老年代爲3G,對象年齡竟然只有9(致使new 區域進入到old區域的速度太快,old區域很快被撐滿,頻繁old gc),考慮到我這邊的對象在一段時間內(幾分鐘)確定會消亡,因而先進行一次調優嘗試cookie
將年齡調成無窮,調大young區網絡
對應的jvm參數爲app
-Xms14g -Xmx14g -Xss512k -XX:PermSize=384m -XX:MaxPermSize=384m -XX:NewSize=12g -XX:MaxNewSize=12g -XX:SurvivorRatio=18 -XX:MaxDirectMemorySize=2g -XX:+UseParNewGC -XX:ParallelGCThreads=4 -XX:MaxTenuringThreshold=30 -XX:+UseConcMarkSweepGC -XX:+DisableExplicitGC -XX:+UseCMSInitiatingOccupancyOnly -XX:+ScavengeBeforeFullGC -XX:+UseCMSCompactAtFullCollection -XX:+CMSParallelRemarkEnabled -XX:CMSFullGCsBeforeCompaction=9 -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSClassUnloadingEnabled -XX:SoftRefLRUPolicyMSPerMB=0 -XX:-ReduceInitialCardMarks -XX:+CMSPermGenSweepingEnabled -XX:CMSInitiatingPermOccupancyFraction=70
結果old區域一直爲空,可是yong gc時間拉長許多,平均每次都要0.2s的時間,比以前的new gc多了整整三倍,是沒法接受的 jvm
關於設置-XX:MaxTenuringThreshold 大於15,在jdk1.7某個版本以前表示是無窮大,以後無論設置成多少,都是15,jdk1.8以後大於15直接報錯 見 http://mail.openjdk.java.net/pipermail/hotspot-gc-dev/2008-May/000309.html優化
將XX:MaxTenuringThreshold 調整回來,調整成最大的值15(大於15即對象長命百歲),因爲以前cms 在old gc花的時間比較多,因此這裏嘗試的serial oldgoogle
對應的jvm參數爲
-Xms14g -Xmx14g -Xss512k -XX:PermSize=384m -XX:MaxPermSize=384m -XX:NewSize=12g -XX:MaxNewSize=12g -XX:SurvivorRatio=18 -XX:MaxDirectMemorySize=2g -XX:+UseParNewGC -XX:ParallelGCThreads=4 -XX:MaxTenuringThreshold=15
發現效果確實比較明顯,new gc的時間縮小了兩倍左右,而且old gc的時間也從原來的15000ms 縮小到1500ms
仔細研究了一下第二種調優方式的組合,yong區域使用parNew 的方式,old區域使用serial old 的方式,若是在其餘條件都相同的條件下使用parNew+cms的方式,old gc的時間會不會大幅度縮短?畢竟cms仍是比較先進的收集器,因而分析了一下cms的幾個階段,有兩個階段是stop the world的,一個是初始化標記(intial mark),一個是重複標記(cms remark),重複標記的緣由是從cms old gc開始的那一刻到開始清除可能不少對象的狀態都產生了變化,因此這個時候須要暫停用戶全部的線程,來一次標記再清除
查看gc日誌,發現remark的時間比較長,日誌上下文以下
從新標記竟然用了18s(這一段[1 CMS-remark: 2202742K(3145728K)] 6959670K(9751808K), 18.1769610 secs])!從新標記的時候,old區域的大小是固定的(這裏設置成old區域的70%),按理說每次remark的時間應該都差很少纔對,可是查了不少cms old gc日誌,發現高峯期和低峯期remark的時間相差太大,二者的區別也只有elden區域,由於我這裏elden的設置是比較大的,高峯期的時候,一次cms old開始,到remark之間這段時間,用戶程序會和gc線程同步執行,到remark的時候,eden區極可能已經有大量對象了,若是remark以前能把eden區域的對象都清理一遍,那remark的對象將會少不少不少對吧?google了一把,發現cms有這麼個參數-XX:+CMSScavengeBeforeRemark,這玩意的意思是在remark以前,來一次yong gc,知足咱們的要求,加了這個參數以後,對應的jvm參數爲
-Xms14g -Xmx14g -Xss512k -XX:PermSize=384m -XX:MaxPermSize=384m -XX:NewSize=12g -XX:MaxNewSize=12g -XX:SurvivorRatio=18 -XX:MaxDirectMemorySize=2g -XX:+UseParNewGC -XX:ParallelGCThreads=4 -XX:MaxTenuringThreshold=15 -XX:+CMSScavengeBeforeRemark -XX:+UseConcMarkSweepGC -XX:+DisableExplicitGC -XX:+UseCMSInitiatingOccupancyOnly -XX:+ScavengeBeforeFullGC -XX:+UseCMSCompactAtFullCollection -XX:+CMSParallelRemarkEnabled -XX:CMSFullGCsBeforeCompaction=9 -XX:CMSInitiatingOccupancyFraction=70
效果以下
發現old gc的時間縮小到原來cms的1/100,竊喜。接下來分析gc日誌,來驗證個人猜測
能夠看到,在remark以前,來一次yong gc,eden區域從50%降到0(總大小爲10.8G),這些空間若是不消除,那麼cms將會在這些空間上進行很是耗時的標記,最後再看看remark的時間
[1 CMS-remark: 1471831K(2097152K)] 1542365K(14050944K), 0.1264420 secs],降到0.1264420s,和原來相比,整整一百倍的提升。
最後,對於長鏈接,push一類的海量服務端應用,16G內存8核心,推薦的JVM參數以下 jdk 1.7 14g->13g
-Xms13g -Xmx13g -Xss512k -XX:PermSize=384m -XX:MaxPermSize=384m -XX:NewSize=12g -XX:MaxNewSize=12g -XX:SurvivorRatio=18 -XX:MaxDirectMemorySize=2g -XX:+UseParNewGC -XX:ParallelGCThreads=4 -XX:MaxTenuringThreshold=15 -XX:+CMSParallelRemarkEnabled -XX:+CMSScavengeBeforeRemark -XX:+UseConcMarkSweepGC -XX:+DisableExplicitGC -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 -XX:+ScavengeBeforeFullGC -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=9 -XX:+CMSClassUnloadingEnabled -XX:CMSInitiatingPermOccupancyFraction=70 -XX:+ExplicitGCInvokesConcurrent -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationConcurrentTime -XX:+PrintHeapAtGC -Xloggc:/data/applogs/heap_trace.txt -XX:-HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/applogs/HeapDumpOnOutOfMemoryError
JDK1.8
-Xms13g -Xmx13g -Xss512k -XX:MetaspaceSize=384m -XX:MaxMetaspaceSize=384m -XX:NewSize=11g -XX:MaxNewSize=11g -XX:SurvivorRatio=18 -XX:MaxDirectMemorySize=2g -XX:+UseParNewGC -XX:ParallelGCThreads=4 -XX:MaxTenuringThreshold=15 -XX:+UseConcMarkSweepGC -XX:+DisableExplicitGC -XX:+UseCMSInitiatingOccupancyOnly -XX:+ScavengeBeforeFullGC -XX:+CMSScavengeBeforeRemark -XX:+CMSParallelRemarkEnabled -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSClassUnloadingEnabled -XX:SoftRefLRUPolicyMSPerMB=0 -XX:-ReduceInitialCardMarks -XX:+CMSClassUnloadingEnabled -XX:+ExplicitGCInvokesConcurrent -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationConcurrentTime -XX:+PrintHeapAtGC -Xloggc:/data/applogs/heap_trace.txt -XX:-HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/applogs/HeapDumpOnOutOfMemoryError
這樣能夠保證大多數對象在new區域就銷燬,而且到了old區,remark以前先yong gc,而後再來一次cms old gc,將old gc控制在毫秒級別