遊戲伺服器JVM效能調優
最近開始優化頁遊服務端的效能,一些心得總結一下。現在的伺服器硬體越來越好,幾十G記憶體,十幾個CPU。當硬體不是瓶頸的時候,如果讓程式發揮最大效用就成了我們需要考慮的問題。就遊戲伺服器來說,得滿足幾個要求,高負載,低延時。特別是在開服當天,大量使用者會湧進來,可能給伺服器造成壓力。使用Java作為伺服器語言,除了程式本身的效能外,JVM的配置也直接影響到系統性能。
引數調優
- 入門級別的配置一般是:java -server -Xmx5000m Xms5000m
伺服器端的jvm執行程式記得都最好加上 -server 很多預設引數都會根據這個執行模式來優化。這裡設定了最大記憶體和最小記憶體,一般都是配置成相同的,可以減少記憶體申請和伸縮帶來的效能損耗
- 加入垃圾回收演算法的配置:java -server -Xmx5000m -Xms5000m -XX:+UseConcMarkSweepGC -XX:+UseParNewGC
關於垃圾回收的具體演算法介紹我這裡就不詳細描述了,我們都有一個常識,就是儘量減少JVM的full gc的次數和時間,因為full gc 會導致整個系統的暫停(stop the world).為此,我們為老年代選擇了UseConcMarkSweepGC 選擇了併發gc演算法,也為新生代選擇了多執行緒的並行gc演算法UseParNewGC。
- 設定新生代的記憶體大小 java -server -Xmx5000m -Xms5000m -Xmn800m -XX:+UseConcMarkSweepGC -XX:+UseParNewGC
Xmn是新生代的記憶體大小,包括(eden+ 2 survivor space)。這個引數設定直接影響系統的響應速度。在java程式中new一個物件,首先是放在eden區域,eden滿了後,觸發gc,存活下來的物件被拷貝到survivor區。經過若干次yong gc後,如果依然存活下來,就會進入老年代。新生代設定大了,會導致一次yong gc的時間消耗大,設定小了,又會很快滿了,導致yong gc的頻率過高。新生代不宜設定過大,因為新生代大了,老年代的記憶體就小了,老年代記憶體小,會導致full gc發生的頻率變大。Xmn也沒有一個確切的演算法,根據你自身的業務系統決定的。我在設定的遊戲伺服器的時候,一般採用模擬大量併發使用者的行為,調整Xmn的大小,同時監控gc的時間和頻率,選擇一個合適的大小。下面我會提到怎麼用工具來監控gc。
- 設定一些額外的高階引數 java -server -Xmx5000m -Xms5000m -Xmn800m -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+UseCMSCompactAtFullCollection
-XX:CMSInitiatingOccupancyFraction=70
使用CMS進行老年代gc,會容易導致一些記憶體碎片,導致記憶體利用率降低,為此,加入 UseCMSCompactAtFullCollection 可以保證在full gc時進行記憶體壓縮,減少記憶體碎片,系統預設為開啟true。CMSInitiatingOccupancyFraction=70 表示老年代記憶體達到70%時觸發。這個引數要特別小心,預設為68%,設定得過小會導致full gc沒有完成,yong gc的物件遷移過來,導致整個老年代記憶體都滿了
5.-XX:+UseCompressedOops JVM優化之壓縮普通物件指標(CompressedOops),通常64位JVM消耗的記憶體會比32位的大1.5倍,這是因為物件指標在64位架構下,長度會翻倍(更寬的定址)。對於那些將要從32位平臺移植到64位的應用來說,平白無辜多了1/2的記憶體佔用,這是開發者不願意看到的。系統預設為開啟true。
6.-XX:+PrintCommandLineFlags 。這個引數的作用是顯示出VM初始化完畢後所有跟最初的預設值不同的引數及它們的值。
7. -XX:+PrintFlagsFinal 。前一個引數只顯示跟預設值不同的,而這個引數則可以顯示所有可設定的引數及它們的值。
8.-XX:+PrintFlagsInitial 。這個引數顯示在處理引數之前所有可設定的引數及它們的值
<span style="font-family:Microsoft YaHei">$ java -version java version "1.6.0_29" Java(TM) SE Runtime Environment (build 1.6.0_29-b11) Java HotSpot(TM) 64-Bit Server VM (build 20.4-b02, mixed mode) $ java -XX:+PrintFlagsInitial | grep UseCompressedOops bool UseCompressedOops = false {lp64_product} $ java -XX:+PrintFlagsFinal | grep UseCompressedOops bool UseCompressedOops := true {lp64_product} </span>
- 檢視預設值的方式統一為
- java -server -Xmx1024m -Xms1024m -XX:+UseConcMarkSweepGC -XX:+PrintFlagsFinal -version| grep ParallelGCThreads
- JVM預設垃圾回收策略,
- 針對年輕代:
- UseParallelGC true(預設)
- UseParNewGC false
UseSerialGC false
-XX:+UseParallelGC:選擇垃圾收集器為並行收集器。此配置僅對年輕代有效。可以同時並行多個垃圾收集執行緒,但此時使用者執行緒必須停止。
-XX:+UseParNewGC:設定年輕代為多執行緒收集。可與CMS收集同時使用。在serial基礎上實現的多執行緒收集器。
- 針對年老代:
UseConcMarkSweepGC false
-XX:-UseConcMarkSweepGC 對老生代採用併發標記交換演算法進行GC
-XX:+UseParallelOldGC:配置年老代垃圾收集方式為並行收集。當-XX:-UseParallelGC啟用時該項自動啟用
工具
- jstat 用來實時檢視gc的狀態,
用法:jstat -gcutil 程序號 時間(毫秒)。結果如下:
裡面列出每個區間的記憶體大小,新生代gc的次數和時間,老年代gc的次數和時間。這裡都能反映出你的JVM的執行狀況
- jmap 用於檢視java程序的物件狀況
用法:jmap -histo:live 程序id 。可以列印每個類的例項數量,記憶體大小
用法:jmap -dump:format=b,file=log.bin 程序id 這個命令特別有用,可以將jvm的整個記憶體映象拷貝下來,用於分析每個物件佔用的記憶體狀況。當你的java程序崩潰了,用這個方法,可以分析出哪些物件是罪魁禍首
- jstack 用於檢視java程序id的堆疊資訊
用法:jstack 程序id 這個工具對於檢視死迴圈的執行緒很有效,可以直接找出是哪個執行緒在哪個方法內死迴圈了
總結
JVM的引數有很多,大部分我們都不需要去設定和優化。如果你的程式沒有問題,就不要去折騰。如果你要優化,一定要有相應的測試流程來支撐。