? ? ? 前陣對底層賬單系統進行了壓測調優,調優的最后一步--jvm啟動參數中,減小了線程的堆??臻g:-XX:ThreadStackSize=256K,縮減至原來的四分之一,效果明顯,不過并沒有調試其他內存空間及gc相關參數。這次有機會在實際壓測中,調優這一部分內容,筆者以cms收集器為例,將有、無調優配置情況下的壓測結果進行對比,來分析各項調用參數的意義及效果。
準備工作:
1.調用查詢接口的測試jar包,作為dubbo-consumer,依賴了查詢服務的api,測試module基于maven開發,執行maven clean package即可通過編譯得到jar包,本次查詢api使用賬戶查詢接口getUserAccount
2.JMeter:Apache組織開發的基于Java的壓力測試工具,添加測試計劃,線程組容量均為200
方案:
無限次請求查詢接口(保證任意時刻并發量相同),觀察Error%為0,當請求平穩進行時的tps,為該接口吞吐量
實施:
1.jvm中只配置打印gc日志等監控參數
-XX:+PrintGCDetails:打印gc日志詳細信息
-XX:+PrintGCTimeStamps:打印gc發生時相對jvm啟動的時間戳,(后來加入了PrintGCDateStamps,打印gc發生的日期)
-Xloggc:設置gc日志的生成位置
壓測數據穩定后,我們觀察到200線程并發壓力下,tps可達到1200+,99%Line600ms左右,我們觀察一下gc日志
這里截取了一次minor gc的數據,兩次gc發生間隔很短(最短不到1s就發生第二次minor gc),系統默認為新生代只分配了100到200MB左右空間,老年代也就是200到300MB左右,可以看出默認分配的空間并不大,并且由于新生代容量非常小,一直在頻繁發生gc。jvm默認采用PSYoungGen-并行收集器來回收新生代空間。
2.jvm中加入cms收集器,并手動規劃jvm空間分配
-Xms4096M:堆容量初始值
-Xmx4096M:堆容量最大值
-Xmn1024M:新生代容量,所以老年代容量 = 堆容量 - 新生代容量 = 3072M
-Xss256K:線程堆??臻g大小
-XX:MaxDirectMemorySize:Direct Buffer Memory大小
-Djava.awt.headless=true:使用缺少外設的系統配置模式
-Dfile.encoding=UTF-8:設置編碼規范
jmx配置用于遠程管理
-XX:+HeapDumpOutOfMemoryError:當出現OOM時,打印堆快照
-XX:HeapDumpPath:堆快照打印路徑,建議文件后綴設為hprof,可被MAT識別
-XX:+DisableExplicitGC:關閉System.gc()
-XX:SurvivorRatio=1:Eden區與Survivor區的大小比值
-XX:+UserConcMarkSweepGC:使用CMS收集器
-XX:+UserParNewGC:新生代使用ParNew收集器
-XX:+CMSParallelRemarkEnabled:降低標記停頓
-XX+UseCMSCompactAtFullCollection:在full gc的時候,對年老代的壓縮
-XX:CMSFullGCsBeforeCompaction=0:full gc后不壓縮老年代內存空間
-XX:LargePageSizeInBytes:內存頁的大小
-XX:+UseFastAccessorMethods:原始類型的快速優化
-XX:+UseCMSInitiatingOccupancyOnly:使用手動定義初始化定義開始CMS收集,禁止hostspot自行觸發CMS GC
-XX:CMSInitiatingOccupancyFraction=80:老年代使用80%后開始CMS收集
-XX:SoftRefLRUPolicyMSPerMB=0:每兆堆空閑空間中SoftReference的存活時間為0秒
? ? ? ?以上這些配置我們重點關注jvm空間分配相關參數和收集器相關參數,首先擴大了堆空間大小至4G,新生代1G,老年代3G,直觀上系統可以承載更多實例的創建,但是同樣也可能造成因對象引用導致的尋址時間增加。另外,手動配置了使用CMS收集器回收老年代,CMS是一種以最短停頓時間為目標的收集器,使用CMS并不能達到GC效率最高,但由于其擁有并發線程進行標記工作,所以盡可能地降低了GC時服務的停頓時間。而為了保證應用線程不停頓,系統需要更多的CPU資源??偟脕碚f,CMS回收器減少了回收的停頓時間,但是降低了堆空間的利用率,也降低了吞吐量,所以使用該收集器的前提是應用程序對停頓比較敏感,并且有許多生存周期長的對象。我們在此處使用該收集器主要用來觀察手動配置后與默認配置相比有哪些優劣。壓測結果如下
我們發現這種情況下吞吐量有小幅度提高,并且響應時間降低,那么jvm起了哪些作用呢,我們看一下gc日志
eden、s1、s2容量被新生代均分為3份,兩個gc間隔時間為2s左右,已進行了2次full gc,其余時間一直在執行minor gc。兩張圖結合分析,我們發現隨著新生代容量的擴大,jvm創建實例能力略有提高,同樣200個線程的并發壓力,同一時間可以承受更多的請求,那是不是新生代容量越大吞吐量就越高呢,筆者后來將新生代內存調至3G,吞吐量不增反降,說明其二者并不是線性相關。我們此時可以確定的是:jvm內存容量要適當增大,并且內存分配比例要盡可能符合jvm對對象的實際創建情況。
總結:
? ? ? ?jvm參數是靈活使用jvm的關鍵,我們在使用時需要謹慎添加,當然如果搭配的好,至少在系統內部服務層面,性能會有客觀的提升。但這也是想表達的另一個觀點,如何配置jvm內存空間,選擇哪些gc收集器組合,并沒有絕對的標準,絕對的好與不好,都是在多次的權衡、搭配下,產生對你所運行的服務一個相對好的效果。jvm調優路漫漫,吾將繼續求索。