讀古今文學網 > 編寫高質量代碼:改善Java程序的151個建議 > 建議137:調整JVM參數以提升性能 >

建議137:調整JVM參數以提升性能

我們寫的每一段Java程序都要在JVM中運行,如果程序已經優化到了極致,但還是覺得性能比較低,那JVM的優化就要提到日程上來了。不過,由於JVM又是系統運行的容器,所以穩定性也是必須考慮的,過度的優化可能就會導致系統故障頻繁發生,致使系統質量大幅下降。下面提供了四個常用的JVM優化手段,供你在需要時參考。

(1)調整堆內存大小

我們知道,在JVM中有兩種內存:棧內存(Stack)和堆內存(Heap),棧內存的特點是空間比較小,速度快,用來存放對象的引用及程序中的基本類型;而堆內存的特點是空間比較大,速度慢,一般對象都會在這裡生成、使用和消亡。

棧空間是由線程開闢,線程結束,棧空間由JVM回收,因此它的大小一般不會對性能有太大的影響,但是它會影響系統的穩定性,在超過棧內存的容量時,系統會報StackOverflowError錯誤。可以通過\"java-Xss<size>\"設置棧內存大小來解決此類問題。

堆內存的調整不能太隨意,調整得太小,會導致Full GC頻繁執行,輕則導致系統性能急速下降,重則導致系統根本無法使用;調整得太大,一則是浪費資源(當然,若設置了最小堆內存則可以避免此問題),二則是產生系統不穩定的情況,例如在32位的機器上設置超過1.8GB的內存就有可能產生莫名其妙的錯誤。設置初始化堆內存為1GB(也就是最小堆內存),最大堆內存為1.5GB可以用如下的參數:


java-Xmx1536m-Xms1024m


(2)調整堆內存中各分區的比例

JVM的堆內存包括三部分:新生區(Young Generation Space)、養老區(Tenure generation space)、永久存儲區(Permanent Space),其中新生成的對象都在新生區,它又分為伊甸區(Eden Space)、倖存0區(Survivor 0 Space)和倖存1區(Survivor 1 Space),當在程序中使用了new關鍵字時,首先在伊甸區生成該對象,如果伊甸區滿了,則用垃圾回收器先進行回收,然後把剩餘的對象移動到倖存區(0區或1區),可如果倖存區也滿了呢?垃圾回收器會再回收一次,然後再把剩餘的對象移動到養老區,那要是養老區也滿了呢?此時就會觸發Full GC(這是一個非常危險的動作,JVM會停止所有的執行,所有系統資源都會讓位給垃圾回收器),會對所有的對象過濾一遍,檢查是否有可以回收的對象,如果還是沒有的話,就拋出OutOfMemoryError錯誤,系統不幹了!

清楚了這個原理(若還是不清楚,請看看《JVM Specification》),那我們就可以思考一下如何提升性能了:若擴大新生區,勢必會減少養老區,這就可能產生不穩定的情況,一般情況下,新生區和養老區的比例為1:3左右,設置命令如下:


java-XX:NewSize=32m-XX:MaxNewSize=640m-XX:MaxPermSize=1280m-XX:NewRatio=5


該配置指定新生代初始化為32MB(也就是新生區最小內存為32M),最大不超過640MB,養老區最大不超過1280MB,新生區和養老區的比例為1:5。

(3)變更GC的垃圾回收策略

Java程序性能的最大障礙就是垃圾回收,我們不知道它何時會發生,也不知道它會執行多長時間,但是我們可以想辦法改變它對系統的影響,比如啟用並行垃圾回收、規定並行回收的線程數量等,命令格式如下:


java-XX:+UseParallelGC-XX:ParallelGCThreads=20


這裡啟用了並行垃圾收集機制,並且定義了20個收集線程(默認的收集線程等於CPU的數量),這對多CPU的系統是非常有幫助的,可以大大減少垃圾回收對系統的影響,提高系統性能。

當然,垃圾回收的策略還有很多屬性可以修改,比如UseSerialGC(啟用串行GC,默認值)、ScavengeBeforeFullGC(新生代GC優先於Full GC執行)、UseConcMarkSweepGC(對老生代採用並發標記交換算法進行GC)等,這些參數需要在系統中逐步調試。

(4)更換JVM

如果所有的JVM優化都不見效,那只有使用最後一招了:更換JVM,目前市面上比較流行的JVM有三個產品:Java HotSpot VM、Oracle JRockit JVM、IBM JVM,其中HotSpot是我們經常使用的,穩定性、可靠性都不錯;JRockit則以效率著稱,性能是它的優勢,但在決定使用該JVM之前一定要做好全面的系統測試,它的某些行為可能會在JRockit上產生Bug;IBM JVM也比較穩定,而且它在AIX系統上的表現要遠遠好於其他操作系統。

JVM的優化不能像程序優化一樣,找到Bug就可以立刻解決,JVM的優化一定是要循序漸進的,參數設置不可激進,特別是需要優化多個參數時,一定要逐步實施,確保每個優化步驟都達到了預期目標,否則會對整個系統的穩定性產生較大的風險。需要提醒的是以上帶有\"-XX\"的JVM參數可能是不健壯的,SUN也不推薦使用,可能後續會在沒有通知的情況下就不再支持它了,但是它又非常好用,這需要在系統升級、遷移時慎重考慮。