1. 程式人生 > >遊戲伺服器JVM效能調優

遊戲伺服器JVM效能調優

最近開始優化頁遊服務端的效能,一些心得總結一下。現在的伺服器硬體越來越好,幾十G記憶體,十幾個CPU。當硬體不是瓶頸的時候,如果讓程式發揮最大效用就成了我們需要考慮的問題。就遊戲伺服器來說,得滿足幾個要求,高負載,低延時。特別是在開服當天,大量使用者會湧進來,可能給伺服器造成壓力。使用Java作為伺服器語言,除了程式本身的效能外,JVM的配置也直接影響到系統性能。

引數調優

  1. 入門級別的配置一般是:java -server -Xmx5000m Xms5000m

    伺服器端的jvm執行程式記得都最好加上 -server 很多預設引數都會根據這個執行模式來優化。這裡設定了最大記憶體和最小記憶體,一般都是配置成相同的,可以減少記憶體申請和伸縮帶來的效能損耗

  2. 加入垃圾回收演算法的配置:java -server -Xmx5000m -Xms5000m -XX:+UseConcMarkSweepGC -XX:+UseParNewGC

    關於垃圾回收的具體演算法介紹我這裡就不詳細描述了,我們都有一個常識,就是儘量減少JVM的full gc的次數和時間,因為full gc 會導致整個系統的暫停(stop the world).為此,我們為老年代選擇了UseConcMarkSweepGC 選擇了併發gc演算法,也為新生代選擇了多執行緒的並行gc演算法UseParNewGC。

  3. 設定新生代的記憶體大小 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。

  4. 設定一些額外的高階引數 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>
  5. 檢視預設值的方式統一為 
  6. java -server -Xmx1024m -Xms1024m -XX:+UseConcMarkSweepGC -XX:+PrintFlagsFinal -version| grep ParallelGCThreads
  7. JVM預設垃圾回收策略,
  8. 針對年輕代:
  9. UseParallelGC true(預設)
  10. UseParNewGC false
    UseSerialGC false
    -XX:+UseParallelGC:選擇垃圾收集器為並行收集器。此配置僅對年輕代有效。可以同時並行多個垃圾收集執行緒,但此時使用者執行緒必須停止。
    -XX:+UseParNewGC:設定年輕代為多執行緒收集。可與CMS收集同時使用。在serial基礎上實現的多執行緒收集器。
    1. 針對年老代:
    UseParallelOldGC true(預設)
    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的引數有很多,大部分我們都不需要去設定和優化。如果你的程式沒有問題,就不要去折騰。如果你要優化,一定要有相應的測試流程來支撐。