標籤:epo 除了 圖形 出現 處理 是什麼 serial world network
寫在之前的話
最近在工作中總是遇到服務的QPS在壓測的時間比較低的情況。於是就開始了效能最佳化之旅,這個過程是很是曲折。一開始的時候認為是服務的商務邏輯比較多,大部分的時間都花在最佳化商務邏輯,減少與其他服務和資料庫等介面的調用,使用緩衝等方式提高效能。上述的工作都是對效能有提升的,但是對於一個熟練的農民工而言,對語言的特性已經成竹在胸,很難通過最佳化一兩句代碼獲得質的提升。由於目前都是採用微服務的開發方式,JVM的參數是自己設定的,就想著在這個方面去最佳化一下。
通過在壓測過程中檢測(使用jstat工具)gc的次數及時間來分析,這個服務在壓力測試中的gc次數已經到了影響提升效能的關鍵因素。於是便著手減少gc的次數。
本次最佳化的目的有兩個:
1、將轉移到老年代的對象數量降低到最小;
2、減少full GC的執行時間;
從網上看了一些JVM調優的方法,其中一位仁兄分享的程式調優方法覺得跟自己的情況特別服務,特別的在這裡跟大家分享下:
“
一切都是為了這一步,調優,在調優之前,我們需要記住下面的原則:
1、多數的Java應用不需要在伺服器上進行GC最佳化;
2、多數導致GC問題的Java應用,都不是因為我們參數設定錯誤,而是代碼問題;
3、在應用上線之前,先考慮將機器的JVM參數設定到最優(最適合);
4、減少建立對象的數量;
5、減少使用全域變數和大對象;
6、GC最佳化是到最後不得已才採用的手段;
7、在實際使用中,分析GC情況最佳化代碼比最佳化GC參數要多得多;
為了達到上面的目的,一般地,你需要做的事情有:
1、減少使用全域變數和大對象;
2、調整新生代的大小到最合適;
3、設定老年代的大小為最合適;
4、選擇合適的GC收集器;
”
JVM堆棧組成及劃分
有了上面的思路,下面我們一起來瞭解下JVM堆棧的結構。JVM堆棧由三部分組成:新生代、老年代和永久代。形象的描述了各個區的劃分:
我們可以看出堆記憶體空間的結構
1. JVM堆記憶體由三大區組成:新生代(Yong Generation)、老年代(Tenured Generation)和永久帶(Perm Generation);
2. 新生代由三部分組成:一個Eden區、兩個Survivor區;
3. 兩個Survivor區一個叫From,一個叫To,從字面叫法上可以看出兩個的作用;
4. Eden和Survivor預設比例是8:1。一般情況下,新建立的對象都會被分配到Eden區(一些大對象特殊處理),這些對象經過第一次Minor GC後,如果仍然存活,將會被移到Survivor區。對象在Survivor區中每熬過一次Minor GC,年齡就會增加1歲,當它的年齡增加到一定程度時,就會被移動到年老代中。
GC過程中對象的移動過程
在GC開始的時候,對象只會存在於Eden區和名為“From”的Survivor區,Survivor區“To”是空的。緊接著進行GC,Eden區中所有存活的對象都會被複製到“To”,而在“From”區中,仍存活的對象會根據他們的年齡值來決定去向。年齡達到一定值(年齡閾值,可以通過-XX:MaxTenuringThreshold來設定)的對象會被移動到年老代中,沒有達到閾值的對象會被複製到“To”地區。經過這次GC後,Eden區和From區已經被清空。這個時候,“From”和“To”會交換他們的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎樣,都會保證名為To的Survivor地區是空的。Minor GC會一直重複這樣的過程,直到“To”區被填滿,“To”區被填滿之後,會將所有對象移動到年老代中。
JVM運行時關鍵參數設定
參數名稱 |
說明 |
Tip |
-Xms |
堆的最小值 |
32位系統 下,一般限制在1.5G~2G;64為作業系統對記憶體無限制 |
-Xmx |
堆空間的最大值 |
32位系統 下,一般限制在1.5G~2G;64為作業系統對記憶體無限制 |
-Xmn |
年輕代大小 |
整個堆大小=年輕代大小 + 年老代大小 + 持久代大小 。持久代一般固定大小為64m,所以增大年輕代後,將會減小年老代大小。此值對系統效能影響較大,Sun官方推薦配置為整個堆的3/8。 |
-Xss |
設定每個線程的堆棧大小 |
JDK5.0以後每個線程堆棧大小為1M,以前每個線程堆棧大小為256K。更具應用的線程所需記憶體大小進行調整。在相同物理內 存下,減小這個值能產生更多的線程。但是作業系統對一個進程內的線程數還是有限制的,不能無限產生,經驗值在3000~5000左右。 |
-Xms |
堆的最小值 |
32位系統 下,一般限制在1.5G~2G;64為作業系統對記憶體無限制 |
有關年輕代的JVM參數
1) -XX:NewSize和-XX:MaxNewSize
用於設定年輕代的大小,建議設為整個堆大小的1/3或者1/4,兩個值設為一樣大。
2) -XX:SurvivorRatio
用於設定Eden和其中一個Survivor的比值,這個值也比較重要。設定年輕代中Eden區與Survivor區的大小比值。設定為4,則兩個Survivor區與一個Eden區的比值為2:4,一個Survivor區占整個年輕代的1/6
3) -XX:NewRatio=4
設定年輕代(包括Eden和兩個Survivor區)與年老代的比值(除去持久代)。設定為4,則年輕代與年老代所佔比值為1:4,年輕代占整個堆棧的1/5
4) -XX:+PrintTenuringDistribution
這個參數用於顯示每次Minor GC時Survivor區中各個年齡段的對象的大小。
5) -XX:MaxPermSize=16m
建議設定持久代大小為16m。
6) -XX:InitialTenuringThreshol和-XX:MaxTenuringThreshold
用於設定晉陞到老年代的對象年齡的最小值和最大值,每個對象在堅持過一次Minor GC之後,年齡就加1。
-XX:MaxTenuringThreshold=0。設定垃圾最大年齡。如果設定為0的話,則年輕代對象不經過Survivor區,直接進入年老代。對於年老代比較多的應用,可以提高效率。如果將此值設定為一個較大值,則年輕代對象會在Survivor區進行多次複製,這樣可以增加對象再年輕代的存活時間,增加在年輕代即被回收的概論。
輸送量收集器
1) -XX:+UseSerialGC
啟用串列垃圾收集器 例如單線程面向輸送量的垃圾收集器 推薦用於只有單個CPU的JVM
2) -XX:+UseParallelGC
使用多線程並存執行年輕代垃圾收集
3) -XX:+UseParallelOldG
除了啟用年輕代並行垃圾收集,也啟用了年老代並行垃圾收集
4) -XX:ParallelGCThreads
-XX:ParallelGCThreads=<value> 指定並行垃圾收集的線程數量
5) -XX:-UseAdaptiveSizePolicy
垃圾收集器能將堆大小動態變動像GC設定一樣應用到不同的堆地區,只要有證據表明這些變動將能提高GC效能
6) -XX:GCTimeRatio
-XX:GCTimeRatio=<value>指定JVM輸送量要達到的目標值
指定目標應用程式線程的執行時間和總的程式執行時間達到N/(N+1)的目標比值
通過-XX:GCTimeRatio=9我們要求應用程式線程在整個執行時間中至少9/10是活動的(因此,GC線程佔用其餘1/10)
-XX:GCTimeRatio的預設值是99,也就是說,應用程式線程應該運行至少99%的總執行時間
7) -XX:MaxGCPauseMillis
通過-XX:GCTimeRatio=<value>告訴JVM最大暫停時間目標值[ms為單位]
CMS收集器
1) -XX:+UseConcMarkSweepGC
啟用CMS收集器 JVM預設使用的是平行處理器
2) -XX:UseParNewGC
使用CMS收集器時 啟用年輕代使用多線程並存執行記憶體回收
注意:
最新的JVM版本,當使用-XX:+UseConcMarkSweepGC時,-XX:UseParNewGC會自動開啟。
因此,如果年輕代的並行GC不想開啟,可以通過設定-XX:-UseParNewGC來關掉。
3) -XX:+CMSConcurrentMTEnabled
並發的CMS階段以多線程執行 預設開啟
4) -XX:ConcGCThreads
-XX:ConcGCThreads=<value>定義並發CMS中啟動並執行線程數
如果還標誌未設定,JVM會根據並行收集器中的-XX:ParallelGCThreads參數的值來計算出預設的並行CMS線程數。該公式是ConcGCThreads = (ParallelGCThreads + 3)/4。因此,對於CMS收集器
-XX:ParallelGCThreads標誌不僅影響“stop-the-world”垃圾收集階段,還影響並發階段。
5) -XX:CMSInitiatingOccupancyFraction
-XX:CMSInitiatingOccupancyFraction=來設定,該值代表老年代堆空間的使用率。比如,value=75意味著第一次CMS垃圾收集會在老年代被佔用75%時被觸發。通常CMSInitiatingOccupancyFraction的預設值為68(之前很長時間的經曆來決定的)。
6) -XX:+UseCMSInitiatingOccupancyOnly
命令JVM不基於運行時收集的資料來啟動CMS垃圾收集周期
JVM通過CMSInitiatingOccupancyFraction的值進行每一次CMS收集,而不僅僅是第一次
7) -XX:+CMSClassUnloadingEnabled
CMS預設不會對永久代進行記憶體回收 如果希望對永久代進行記憶體回收 可以設定此標誌
注意,即使沒有設定這個標誌,一旦永久代耗盡空間也會嘗試進行記憶體回收,但是收集不會是並行的,而再一次進行Full GC。
8) -XX:+CMSIncrementalMode
開啟CMS收集器的增量模式
ps:增量模式會經常暫停CMS過程 以便對應用程式作出完全的讓步
9) -XX:+ExplicitGCInvokesConcurrent和-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses
1.-XX:+ExplicitGCInvokesConcurrent
命令JVM無論什麼時候調用系統GC,都執行CMS GC,而不是Full GC
2.-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses
保證當有系統GC調用時,永久代也被包括進CMS記憶體回收的範圍內。因此,通過使用這些標誌,我們可以防止出現意料之外的”stop-the-world”的系統GC。
10) -XX:+DisableExplicitGC
該標誌將告訴JVM完全忽略系統的GC調用(不管使用的收集器是什麼類型)
總結
JVM參數是死的,你的服務所對應的情境才是活的。在調優的過程中需要根據自己的業務需要不斷的調整參數、測試、再調整的迭代方式直到效能達到最優。
推薦大家使用jstat這款Java自動的檢測GC的工具,對於查看系統的GC資訊有很大的協助。
參考資料
官方提供的JVM參數一覽表:
http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html
51544977
JVM調優總結