前面咱們學習了整個JVM系列,最終目標的不只僅是瞭解JVM的基礎知識,也是爲了進行JVM性能調優作準備。這篇文章帶領你們學習JVM性能調優的知識。面試
性能調優
性能調優包含多個層次,好比:架構調優、代碼調優、JVM調優、數據庫調優、操做系統調優等。數據庫
架構調優和代碼調優是JVM調優的基礎,其中架構調優是對系統影響最大的。緩存
性能調優基本上按照如下步驟進行:明確優化目標、發現性能瓶頸、性能調優、經過監控及數據統計工具得到數據、確認是否達到目標。性能優化
什麼時候進行JVM調優
遇到如下狀況,就須要考慮進行JVM調優了:服務器
- Heap內存(老年代)持續上漲達到設置的最大內存值;
- Full GC 次數頻繁;
- GC 停頓時間過長(超過1秒);
- 應用出現OutOfMemory 等內存異常;
- 應用中有使用本地緩存且佔用大量內存空間;
- 系統吞吐量與響應性能不高或降低。
JVM調優的基本原則
JVM調優是一個手段,但並不必定全部問題均可以經過JVM進行調優解決,所以,在進行JVM調優時,咱們要遵循一些原則:微信
- 大多數的Java應用不須要進行JVM優化;
- 大多數致使GC問題的緣由是代碼層面的問題致使的(代碼層面);
- 上線以前,應先考慮將機器的JVM參數設置到最優;
- 減小建立對象的數量(代碼層面);
- 減小使用全局變量和大對象(代碼層面);
- 優先架構調優和代碼調優,JVM優化是不得已的手段(代碼、架構層面);
- 分析GC狀況優化代碼比優化JVM參數更好(代碼層面);
經過以上原則,咱們發現,其實最有效的優化手段是架構和代碼層面的優化,而JVM優化則是最後不得已的手段,也能夠說是對服務器配置的最後一次「壓榨」。markdown
JVM調優目標
調優的最終目的都是爲了令應用程序使用最小的硬件消耗來承載更大的吞吐。jvm調優主要是針對垃圾收集器的收集性能優化,令運行在虛擬機上的應用可以使用更少的內存以及延遲獲取更大的吞吐量。架構
- 延遲:GC低停頓和GC低頻率;
- 低內存佔用;
- 高吞吐量;
其中,任何一個屬性性能的提升,幾乎都是以犧牲其餘屬性性能的損爲代價的,不可兼得。具體根據在業務中的重要性肯定。
JVM調優量化目標
下面展現了一些JVM調優的量化目標參考實例:
- Heap 內存使用率 <= 70%;
- Old generation內存使用率<= 70%;
- avgpause <= 1秒;
- Full gc 次數0 或 avg pause interval >= 24小時 ;
注意:不一樣應用的JVM調優量化目標是不同的。
JVM調優的步驟
通常狀況下,JVM調優可經過如下步驟進行:
- 分析GC日誌及dump文件,判斷是否須要優化,肯定瓶頸問題點;
- 肯定JVM調優量化目標;
- 肯定JVM調優參數(根據歷史JVM參數來調整);
- 依次調優內存、延遲、吞吐量等指標;
- 對比觀察調優先後的差別;
- 不斷的分析和調整,直到找到合適的JVM參數配置;
- 找到最合適的參數,將這些參數應用到全部服務器,並進行後續跟蹤。
以上操做步驟中,某些步驟是須要屢次不斷迭代完成的。通常是從知足程序的內存使用需求開始的,以後是時間延遲的要求,最後纔是吞吐量的要求,要基於這個步驟來不斷優化,每個步驟都是進行下一步的基礎,不可逆行之。
JVM參數
JVM調優最重要的工具就是JVM參數了。先來了解一下JVM參數相關內容。
-XX 參數被稱爲不穩定參數,此類參數的設置很容易引發JVM 性能上的差別,使JVM存在極大的不穩定性。若是此類參數設置合理將大大提升JVM的性能及穩定性。
不穩定參數語法規則包含如下內容。
布爾類型參數值:
- -XX:+'+'表示啓用該選項
- -XX:-'-'表示關閉該選項
數字類型參數值:
- -XX:=給選項設置一個數字類型值,可跟隨單位,例如:'m'或'M'表示兆字節;'k'或'K'千字節;'g'或'G'千兆字節。32K與32768是相同大小的。
字符串類型參數值:
- -XX:=給選項設置一個字符串類型值,一般用於指定一個文件、路徑或一系列命令列表。例如:-XX:HeapDumpPath=./dump.core
JVM參數解析及調優
好比如下參數示例:
-Xmx4g –Xms4g –Xmn1200m –Xss512k -XX:NewRatio=4 -XX:SurvivorRatio=8 -XX:PermSize=100m -XX:MaxPermSize=256m -XX:MaxTenuringThreshold=15
上面爲Java7及之前版本的示例,在Java8中永久代的參數-XX:PermSize和-XX:MaxPermSize已經失效。這在前面章節中已經講到。
參數解析:
- -Xmx4g:堆內存最大值爲4GB。
- -Xms4g:初始化堆內存大小爲4GB。
- -Xmn1200m:設置年輕代大小爲1200MB。增大年輕代後,將會減少年老代大小。此值對系統性能影響較大,Sun官方推薦配置爲整個堆的3/8。
- -Xss512k:設置每一個線程的堆棧大小。JDK5.0之後每一個線程堆棧大小爲1MB,之前每一個線程堆棧大小爲256K。應根據應用線程所需內存大小進行調整。在相同物理內存下,減少這個值能生成更多的線程。可是操做系統對一個進程內的線程數仍是有限制的,不能無限生成,經驗值在3000~5000左右。
- -XX:NewRatio=4:設置年輕代(包括Eden和兩個Survivor區)與年老代的比值(除去持久代)。設置爲4,則年輕代與年老代所佔比值爲1:4,年輕代佔整個堆棧的1/5
- -XX:SurvivorRatio=8:設置年輕代中Eden區與Survivor區的大小比值。設置爲8,則兩個Survivor區與一個Eden區的比值爲2:8,一個Survivor區佔整個年輕代的1/10
- -XX:PermSize=100m:初始化永久代大小爲100MB。
- -XX:MaxPermSize=256m:設置持久代大小爲256MB。
- -XX:MaxTenuringThreshold=15:設置垃圾最大年齡。若是設置爲0的話,則年輕代對象不通過Survivor區,直接進入年老代。對於年老代比較多的應用,能夠提升效率。若是將此值設置爲一個較大值,則年輕代對象會在Survivor區進行屢次複製,這樣能夠增長對象再年輕代的存活時間,增長在年輕代即被回收的概論。
新生代、老生代、永久代的參數,若是不進行指定,虛擬機會自動選擇合適的值,同時也會基於系統的開銷自動調整。
可調優參數:
-Xms:初始化堆內存大小,默認爲物理內存的1/64(小於1GB)。
-Xmx:堆內存最大值。默認(MaxHeapFreeRatio參數能夠調整)空餘堆內存大於70%時,JVM會減小堆直到-Xms的最小限制。
-Xmn:新生代大小,包括Eden區與2個Survivor區。
-XX:SurvivorRatio=1:Eden區與一個Survivor區比值爲1:1。
-XX:MaxDirectMemorySize=1G:直接內存。報java.lang.OutOfMemoryError: Direct buffer memory異常能夠上調這個值。
-XX:+DisableExplicitGC:禁止運行期顯式地調用System.gc()來觸發fulll GC。
注意: Java RMI的定時GC觸發機制可經過配置-Dsun.rmi.dgc.server.gcInterval=86400來控制觸發的時間。
-XX:CMSInitiatingOccupancyFraction=60:老年代內存回收閾值,默認值爲68。
-XX:ConcGCThreads=4:CMS垃圾回收器並行線程線,推薦值爲CPU核心數。
-XX:ParallelGCThreads=8:新生代並行收集器的線程數。
-XX:MaxTenuringThreshold=10:設置垃圾最大年齡。若是設置爲0的話,則年輕代對象不通過Survivor區,直接進入年老代。對於年老代比較多的應用,能夠提升效率。若是將此值設置爲一個較大值,則年輕代對象會在Survivor區進行屢次複製,這樣能夠增長對象再年輕代的存活時間,增長在年輕代即被回收的概論。
-XX:CMSFullGCsBeforeCompaction=4:指定進行多少次fullGC以後,進行tenured區 內存空間壓縮。
-XX:CMSMaxAbortablePrecleanTime=500:當abortable-preclean預清理階段執行達到這個時間時就會結束。
在設置的時候,若是關注性能開銷的話,應儘可能把永久代的初始值與最大值設置爲同一值,由於永久代的大小調整須要進行FullGC才能實現。
內存優化示例
當JVM運行穩定以後,觸發了FullGC咱們通常會拿到以下信息:
以上gc日誌中,在發生fullGC之時,整個應用的堆佔用以及GC時間。爲了更加精確需屢次收集,計算平均值。或者是採用耗時最長的一次FullGC來進行估算。上圖中,老年代空間佔用在93168kb(約93MB),以此定爲老年代空間的活躍數據。則其餘堆空間的分配,基於如下規則來進行。
- java heap:參數-Xms和-Xmx,建議擴大至3-4倍FullGC後的老年代空間佔用。
- 永久代:-XX:PermSize和-XX:MaxPermSize,建議擴大至1.2-1.5倍FullGc後的永久帶空間佔用。
- 新生代:-Xmn,建議擴大至1-1.5倍FullGC以後的老年代空間佔用。
- 老年代:2-3倍FullGC後的老年代空間佔用。
基於以上規則,則對參數定義以下:
java -Xms373m -Xmx373m -Xmn140m -XX:PermSize=5m -XX:MaxPermSize=5m
延遲優化示例
對延遲性優化,首先須要瞭解延遲性需求及可調優的指標有哪些。
- 應用程序可接受的平均停滯時間: 此時間與測量的Minor
- GC持續時間進行比較。可接受的Minor GC頻率:Minor
- GC的頻率與可容忍的值進行比較。
- 可接受的最大停頓時間:最大停頓時間與最差狀況下FullGC的持續時間進行比較。
- 可接受的最大停頓發生的頻率:基本就是FullGC的頻率。
其中,平均停滯時間和最大停頓時間,對用戶體驗最爲重要。對於上面的指標,相關數據採集包括:MinorGC的持續時間、統計MinorGC的次數、FullGC的最差持續時間、最差狀況下,FullGC的頻率。
如上圖,Minor GC的平均持續時間0.069秒,MinorGC的頻率爲0.389秒一次。
新生代空間越大,Minor GC的GC時間越長,頻率越低。若是想減小其持續時長,就須要減小其空間大小。若是想減少其頻率,就須要加大其空間大小。
這裏以減小了新生代空間10%的大小,來減少延遲時間。在此過程當中,應該保持老年代和持代的大小不變化。調優後的參數以下變化:
java -Xms359m -Xmx359m -Xmn126m -XX:PermSize=5m -XX:MaxPermSize=5m
吞吐量調優
吞吐量調優主要是基於應用程序的吞吐量要求而來的,應用程序應該有一個綜合的吞吐指標,這個指標基於整個應用的需求和測試而衍生出來的。
評估當前吞吐量和目標差距是否巨大,若是在20%左右,能夠修改參數,加大內存,再次從頭調試,若是巨大就須要從整個應用層面來考慮,設計以及目標是否一致了,從新評估吞吐目標。
對於垃圾收集器來講,提高吞吐量的性能調優的目標就是儘量避免或者不多發生FullGC或者Stop-The-World壓縮式垃圾收集(CMS),由於這兩種方式都會形成應用程序吞吐下降。儘可能在MinorGC 階段回收更多的對象,避免對象提高過快到老年代。
調優工具
藉助GCViewer日誌分析工具,能夠很是直觀地分析出待調優勢。可從如下幾方面來分析:
Memory,分析Totalheap、Tenuredheap、Youngheap內存佔用率及其餘指標,理論上內存佔用率越小越好;
Pause,分析Gc pause、Fullgc pause、Total pause三個大項中各指標,理論上GC次數越少越好,GC時長越小越好;
原文連接:《JVM性能調優詳解》
本文參考:
(1)http://www.javashuo.com/article/p-wnvshwvs-ee.html
(2)http://www.javashuo.com/article/p-qvijjyrm-gz.html
《面試官》系列文章:
- 《JVM以內存結構詳解》
- 《面試官,不要再問我「Java GC垃圾回收機制」了》
- 《面試官,Java8 JVM內存結構變了,永久代到元空間》
- 《面試官,不要再問我「Java 垃圾收集器」了》
- 《Java虛擬機類加載器及雙親委派機制》
- 《Java內存模型(JMM)詳解》
- 《Java內存模型相關原則詳解》
- 《JVM性能調優詳解》