JVM調優之經驗

在生產系統中,高吞吐和低延遲一直都是JVM調優的最終目標,但這二者偏偏又是相悖的,魚和熊掌不可兼得,因此在調優以前要清楚舍誰而取誰。通常計算任務和組件服務會偏向高吞吐,而web展現則偏向低延遲纔會帶來更好的用戶體驗。java

本文從性能和經驗上來分享一下JVM參數的設置。web

調優以前能夠先用-XX:+PrintFlagsFinal來查看虛擬機是否默認開啓某參數,不一樣版本的JDK可能虛擬機默認開啓的參數也略有不一樣,新學習一條神奇的參數的時候能夠先去查找一下參數是否默認開啓了。bash

$ java -server -XX:+PrintCommandLineFlags |grep XXXXXXX

也能夠經過jinfo口令 jinfo -flags [pid]來查看併發

GC策略

目前來看仍是CMS當道,吞吐率和響應時間闊以兼顧,G1嘛,雞丸雞丸,至今並無展示出one的實力,不過據貴裏某P8講G1在大堆(20G+)下表現更突出,停頓會顯著下降,可能以後隨着高內存愈來愈經濟和普及,G1才能名副其實的稱爲雞one。框架

廢話少說,性能

-XX:+UseConcMarkSweepGC學習

設置CMS作爲垃圾收集,CMS開啓後默認的新生代回收是ParNew,若是CMS出現「Concurrent Mode Failure」了還會啓用Serial Old作備胎。優化


-XX:CMSInitiatingOccupancyFraction=75spa

默認值是68,這個能夠根據實際調優目標來調整,這個參數就比較應開始提到的,調優目標是下降延遲仍是提升吞吐,若是是爲了下降單次GC延遲,那麼這個值闊以再往低了調一些,不過調的過高可能致使老年代剩餘空間不夠招呼併發收集產生的浮動垃圾而頻繁的觸發Full GC。線程


-XX:+UseCMSInitiatingOccupancyOnly

使用CMS的話這個參數必定要加上,必定要加上,必定要加上,重要的事情說三遍,不然虛擬機後面仍是會自做聰明的本身計算上個參數的比值。


-XX:MaxTenuringThreshold=5

默認15,這個值是設置新生代對象存活了多少次young GC後能夠進入老年代,值設的高的話可使老年代增加緩慢,但YGC的次數會明顯增多,若是清楚YGC的執行頻率和大多數對象的最長生命週期,這個值能夠設低些,讓那些對象早點進入老年代。
能夠用-XX:+PrintTenuringDistribution來觀察一段時間,而後調整合適的值。

ps:有一種野路子是此值設爲0,新生代GC次數少,速度快,就是老年代GC會更加頻繁一些,不過也最大利用了併發GC。不過我沒在生產這麼搞過,效果有待驗證。


-XX:+ExplicitGCInvokesConcurrent

這個參數是用來代替,-XX:DisableExplicitGC的,NIO許多地方會顯示的調用System.gc()來觸發一次Full GC。許多時候別的地方優化一萬點都賠不起這兒調上幾回的。ExplicitGCInvokesConcurrent這個參數是配合CMS使用的,開啓後System.gc()仍是會觸發Full GC,不過並非一個徹底的stop-the-world的Full GC,而是併發的CMS GC。


內存設置

如今線上業務系統基本物理內存都是夠用的,不過物盡其用,咱們調優就是爭取讓每M空間都發揮出最大的做用。內存的設置仍是最直觀見效的。

-Xmx500m ,-Xms500m

最大堆內存和最小堆內存,這兩個值要設的一致,避免虛擬機還要動態的計算分配內存空間。
PS:堆也不是越大越好,大堆帶來的後果就是單次GC會較長。


-Xmn250m

新生代大小,非G1收集器能夠設置這個值,G1的官方建議是不要顯示分配新生代和老年代空間大小,由於G1會經過網格化內存來動態分配new/old區,官方認爲不設置new size是最佳實踐。


-Xss2m

每一個線程的棧空間大小,默認值是1m,通常不須要設置,除非有遞歸方法存在可能會爆棧。


-XX:PermSize=128m,-XX:MaxPermSize=256m

JDK8以前永久代的空間設置,Spring框架了大量依賴AOP的實現都用的動態代理生成字節碼,因此設個最大值求保險。
不過JDK8以後取消了永久代,改成元空間(MetaSpace),這塊屬於本地內存,理論上能夠利用系統剩餘的全部內存,不過跑了多個實例的話仍是要設置一下爲妙:

-XX:MetaspaceSize=128m,-XX:MaxMetaspaceSize=256m


-XX:MaxDirectMemorySize=128m

這個屬於對外內存,能夠合理控制大小。Heap區總內存減去一個Survivor區的大小,不宜過大,不然可能heap size + Direct Memory Size把物理內存耗光。


-XX:SurvivorRatio=7

默認是8,新生代中Eden與Survivor的比值,過大的話可能Survivor存不下臨時對象而頻繁觸發分配擔保。能夠根據GC日誌看實際狀況。


PS:
關於內存大小的設置徹底要根據各個機器和應用自身的狀況來設置。
能夠經過jstat -gc [pid] 2000 30,每2s輸出一次一共輸出30次內存狀況,看看各個區域增加的速度,最大空間等數據來修改內存設置。


監控輸出

監控參數仍是須要的,否則有時候線上偶爾OOM了真的很差重現。


-XX:+HeapDumpOnOutOfMemoryError,-XX:HeapDumpPath={path}

OOM的時候會輸出dump快照到{path}目錄,只須要指向目錄,文件名JVM會保持惟一性。


-XX:+PrintGCDetails,-Xloggc:logs/gc.log,-XX:+PrintGCTimeStamps,-XX:+PrintGCDateStamps

打印GC詳細記錄,-XX:+PrintGC 這個口令是簡單GC日誌,爲了更容易定位問題,咱們開啓Details模式,-Xloggc是把gc日誌輸出到指定文件。
-XX:+PrintGCTimeStamps顯示的時間表明JVM啓動至記錄日誌的時間。
-XX:+PrintGCDateStamps則會添加上每行信息的絕對日期。
其實開啓了-Xloggc的話會隱式的開啓-XX:+PrintGCTimeStamps,不過爲了防止各版本JVM改動差別,仍是顯示的設置出來保險。


-XX:-OmitStackTraceInFastThrow

這是個比較容易被忽略的參數,而沒有經驗的話又每每很難定位到緣由。
JDK5以後JVM對異常作了一個優化,對於一些頻繁拋出的異常,JIT從新編譯後會拋出沒有堆棧信息的異常,-server模式下是默認開啓的,所以在頻繁拋出某個異常一段時間後,該優化開始起做用,即只拋出沒有堆棧的異常信息。
但因爲該優化是JIT編譯後才啓用的,因此開始該異常的拋出是有完整堆棧信息的,但運行一段時間可能發現沒有任何堆棧信息,很難定位,初次遇到很容易摸不到頭腦。
可使用-XX:-OmitStackTraceInFastThrow來關閉該項優化。

原文連接來源:lousama

相關文章
相關標籤/搜索