優(yōu)化 Java 垃圾收集器改進(jìn)系統(tǒng)性能![]() |
![]() |
![]() |
|
級(jí)別: 高級(jí)
李 曉華
(xiaohual@cn.ibm.com), IBM CDL ODSC部門工程師
2006 年 11 月 30 日
在系統(tǒng)的性能測(cè)試過程中,當(dāng)系統(tǒng)的處理能力有某種變化趨勢(shì)時(shí), 除了關(guān)于等待隊(duì)列、執(zhí)行線程,EJB 池以及數(shù)據(jù)庫連接池和 Statement Cache 方面的調(diào)優(yōu)外,還要考慮到 Java 垃圾收集器(Garbage Collection,本文簡(jiǎn)稱 GC)對(duì)系統(tǒng)性能的影響。本文介紹了如何分析系統(tǒng)的處理能力和 GC 之間的關(guān)系,以及如何通過改進(jìn) JVM 的配置來優(yōu)化 GC,以提高系統(tǒng)的性能。
某個(gè)大型項(xiàng)目的 CPU100% 的壓力性能測(cè)試, 用以檢查在系統(tǒng)運(yùn)行環(huán)境不正常的情況下,系統(tǒng)可以運(yùn)行到何種程度。測(cè)試過程是: 請(qǐng)求測(cè)試的模擬器向系統(tǒng)不斷發(fā)出大量請(qǐng)求, 系統(tǒng)接受由模擬器發(fā)出的請(qǐng)求,然后將請(qǐng)求置于一個(gè)任務(wù)池中,如果當(dāng)前有空閑的線程,則該線程會(huì)從任務(wù)池中取出一個(gè)任務(wù)進(jìn)行處理,如果沒有空閑的線程,則該任務(wù)一直會(huì)待在任務(wù)池中,直到有空閑的線程來處理它。因此,任務(wù)池的隊(duì)列的長(zhǎng)度從某種意義上可以代表整個(gè)系統(tǒng)的處理能力,任務(wù)池隊(duì)列的長(zhǎng)度用 Q 值來表示,如果 Q 值超出了一定限額,將會(huì)有流量控制的線程將超出限額的待處理任務(wù)丟棄,以保證系統(tǒng)的穩(wěn)定性。
整個(gè)測(cè)試要求得到系統(tǒng)所在服務(wù)器的負(fù)載達(dá)到將近 100% 時(shí),系統(tǒng)的吞吐量,相應(yīng)時(shí)間以及在超負(fù)荷下業(yè)務(wù)請(qǐng)求成功率。
在測(cè)試過程中,任務(wù)池中累積的任務(wù)數(shù)起伏很大,正常時(shí)累積的任務(wù)數(shù)很小,但是每隔一段時(shí)間會(huì)累積大量的任務(wù)。由于累積的任務(wù)數(shù)超出任務(wù)池流量控制所定義的限額,所以每隔一段時(shí)間,大量的待處理任務(wù)被清除。因此測(cè)試結(jié)束后得到的在超負(fù)荷下業(yè)務(wù)請(qǐng)求成功率也不是很理想。
一臺(tái)AIX服務(wù)器(4CPU,4GMemory)來部署本W(wǎng)eb應(yīng)用程序;Web應(yīng)用程序部署在中間件應(yīng)用服務(wù)器上;部署了一個(gè)節(jié)點(diǎn)(Node),只配置一個(gè)應(yīng)用服務(wù)器實(shí)例(Instance),沒有做Cluster部署。
![]() ![]() |
![]()
|
檢測(cè)WebSphere Application Server上的Web Container,EJB Container , ORB Service,數(shù)據(jù)庫連接池等設(shè)置均合理,然后懷疑問題的現(xiàn)象是不是與系統(tǒng)GC有關(guān)。當(dāng)前Java Virtual Machine的配置為: Initial Heap Size:256 , Maximum Heap Size: 3072。
為了驗(yàn)證任務(wù)池中累積的任務(wù)數(shù)的大幅度變化和系統(tǒng)GC是否存在一定的關(guān)系,通過對(duì)任務(wù)池的累積任務(wù)數(shù)和系統(tǒng)GC進(jìn)行采樣, 將采樣后的數(shù)據(jù)進(jìn)行分析,用以得出二者的關(guān)系。采樣時(shí)遵循Nyquist采樣定例: 采樣頻率要大于被采集對(duì)象的頻率的2倍。 否則,采樣點(diǎn)很可能每次位于被采集對(duì)象的波形的某個(gè)點(diǎn)上,從而不能正確反映被采集對(duì)象的變化規(guī)律。
通過觀察,發(fā)現(xiàn)任務(wù)池的任務(wù)數(shù)目(以下用Q值代替)的變化周期大概是5到6秒,因此根據(jù)Nyquist采樣定例,采樣的時(shí)間間隔不能超過2-3秒,所以按照每秒來采樣。 測(cè)試時(shí)間是3分鐘,采樣180次,系統(tǒng)的當(dāng)前負(fù)載率是99%。采樣圖如下所示:
圖一 任務(wù)池Q值的采樣圖

由于系流量控制要求的限額是450個(gè)任務(wù),也就是任務(wù)池中最多能累積450個(gè)任務(wù),當(dāng)任務(wù)池中累積的任務(wù)數(shù)超過450時(shí),多余的任務(wù)會(huì)被流量控制直接丟棄,從上圖可以看出,系統(tǒng)的Q值在很多時(shí)刻都大于450,因此多次被丟棄任務(wù),從而導(dǎo)致了任務(wù)請(qǐng)求成功率不高。
系統(tǒng)GC的采樣
1: 在WebSphere Administrative Console上, 點(diǎn)擊進(jìn)入:Servers, 然后Application servers > server1 > Process Definition > Java Virtual Machine, 在Configuration面板上,選上Verbose garbage collection選項(xiàng)。
圖二 WebSphere Application Server的JVM配置示圖

2:進(jìn)入<%WebSphere Application Server的安裝目錄%>/profiles/<%所在的profiles%>/logs/ <%所在的Server%>, 可以看到native_stderr.log文件,將其清空
3:在高負(fù)載的條件下,進(jìn)行高壓測(cè)試3分鐘
4:將native_stderr.log文件拷貝出來,用GCCollector工具進(jìn)行分析,其中native_stderr.log文件上記錄了系統(tǒng)GC的數(shù)據(jù)。
5:安裝GCCollector工具: 下載完GCCollector.zip后,解壓縮,將里面Lib里的3個(gè)文件 jfreechart-1.0.0-rc1.jar,jcommon-1.0.0-rc1.jar 和GCCollector.jar拷貝至JRE的lib目錄下,然后在命令行控制臺(tái)上進(jìn)入JRE的安裝目錄,而后運(yùn)行: java -classpath jfreechart-1.0.0-rc1.jar;jcommon-1.0.0-rc1.jar -jar GCCollector.jar。
接著可以看到GCCollector的用戶界面,在它的Parser菜單中選擇JRE的版本,而后在File菜單中選擇并打開剛才拷出的native_stderr.log文件。
下圖是在高負(fù)載情況下,系統(tǒng)在當(dāng)前配置下的GC分析圖。
圖三 系統(tǒng)的GC分析圖

由圖三可以看出,系統(tǒng)沒有內(nèi)存泄漏的現(xiàn)象,每次GC所花的時(shí)間為220ms左右,從GCCollector的Spreadsheet可以查到,GC的時(shí)間周期為5-6 s,每次具體GC發(fā)生的時(shí)間,每次GC所花的時(shí)間。
6:同樣遵循Nyquist采樣定例,對(duì)GC進(jìn)行采樣,采樣后的數(shù)據(jù)同任務(wù)池中Q值的采樣數(shù)據(jù)進(jìn)行比較和分析,得出兩者之間存在著密切的關(guān)系。下圖為任務(wù)池Q值和GC數(shù)據(jù)采樣分析圖,由圖中可以看出,每次任務(wù)池的Q值大幅度增長(zhǎng)時(shí),系統(tǒng)剛好發(fā)生GC。二者的時(shí)間和周期幾乎可以完全匹配。 因此可以初步下一個(gè)結(jié)論,由于系統(tǒng)的GC,導(dǎo)致了系統(tǒng)在某些時(shí)刻不能有足夠的能力來處理請(qǐng)求,因此任務(wù)池的Q值在這些時(shí)候會(huì)因?yàn)槿蝿?wù)的大量累積而巨幅漲大,即而超出限額的任務(wù)被流控所清除,導(dǎo)致了在超負(fù)荷下任務(wù)請(qǐng)求成功率不是很理想。
圖四 任務(wù)池Q值和GC數(shù)據(jù)采樣分析圖

![]() ![]() |
![]()
|
當(dāng)前的GC發(fā)生的頻率和每次所花的時(shí)間還算正常,但是如果每次GC所花的CPU時(shí)間能減少,就能空出系統(tǒng)更多的能力處理任務(wù)池里的任務(wù),用以降低任務(wù)池中的任務(wù)數(shù),使得Q值基本上位于任務(wù)池的限額以下,這樣可以提高在超負(fù)荷下業(yè)務(wù)請(qǐng)求的總的成功率。
當(dāng)前Java Virtual Machine的配置為: Initial Heap Size:256 , Maximum Heap Size: 3072。相對(duì)而言, Maximum Heap Size設(shè)的有點(diǎn)偏大。對(duì)于不同的應(yīng)用程序,最優(yōu)化堆大小的設(shè)置都有可能不同。堆設(shè)置變大,GC的頻率會(huì)降低,應(yīng)用程序會(huì)運(yùn)行更長(zhǎng)的時(shí)間后,才會(huì)進(jìn)行GC,帶來的就是每次GC也會(huì)花更長(zhǎng)的時(shí)間。從而GC調(diào)用占用系統(tǒng)更長(zhǎng)的時(shí)間,使系統(tǒng)沒有足夠的能力處理請(qǐng)求,使得此刻任務(wù)池中的任務(wù)累積很多,Q值很大,形成很高的峰值,超過流量控制的限額,超過限額數(shù)的任務(wù)被流量控制給直接丟棄,從而導(dǎo)致總的成功率不高。
因此,必須降低堆大小,使得GC的頻率增大,每次GC所花時(shí)間降低,從而降低Q的峰值,使之位于系統(tǒng)的流量控制的范圍之內(nèi),從而提高業(yè)務(wù)請(qǐng)求的總的成功率。
當(dāng)前的流量控制所允許的峰值是450,因此我們需要通過試驗(yàn)來驗(yàn)證,堆大小降低到什么值時(shí),Q值的峰值將低于450,以保證總的成功率。 同時(shí)在峰值低于450的條件下,什么樣的堆大小設(shè)置可以讓系統(tǒng)的性能最佳。 因?yàn)槿绻言O(shè)置過小,會(huì)使得對(duì)象可分配空間變小,從而會(huì)頻繁的使用垃圾收集機(jī)制來釋放內(nèi)存空間,而每次垃圾收集,都會(huì)耗用一定的系統(tǒng)資源。所以,我們要通過試驗(yàn)和監(jiān)控?cái)?shù)據(jù),設(shè)法使的我們所設(shè)置的堆大小能夠使得我們的程序運(yùn)行最優(yōu)化。
通過多次試驗(yàn),我們得出結(jié)論:當(dāng)Java Virtual Machine的配置為: Initial Heap Size:512 , Maximum Heap Size: 1024時(shí),該系統(tǒng)性能最佳。
從WebSphere Administrative Console上,依次點(diǎn)擊Servers->Application Servers,然后選擇需要的server,接著點(diǎn)擊Process Definition->Java Virtual Machine,而后在那里設(shè)置Initial Heap Size:512和Maximum Heap Size: 1024。這時(shí)的配置對(duì)于該應(yīng)用系統(tǒng)相對(duì)較為合理。這時(shí)再次分析3分鐘內(nèi)的GC的數(shù)據(jù),從下圖可以看出,GC的周期縮短了,每1-2秒就會(huì)發(fā)生一次GC,但是每次GC所花費(fèi)的CPU時(shí)間降低了,平均130ms左右。
圖五 改進(jìn)JVM配置后的GC分析圖

相應(yīng)地,在JVM配置改進(jìn)后,對(duì)任務(wù)池的Q值再進(jìn)行一次采樣,并且和改進(jìn)前的采樣值進(jìn)行比價(jià),采樣圖如下圖六所示,改進(jìn)后任務(wù)池的Q值分布在50-450之間,數(shù)值的起伏相對(duì)改進(jìn)前要均衡些。不再出現(xiàn)忽然間Q值大小漲到500以上的情況,基本在流控的限制范圍之類,進(jìn)而提高了在超負(fù)荷下業(yè)務(wù)請(qǐng)求的總的成功率。
圖六 改進(jìn)JVM配置后和改進(jìn)前的Q值采樣比較圖

![]() ![]() |
![]()
|
通過本文,我們可以看到系統(tǒng)JVM的配置和系統(tǒng)的性能有著比較大的關(guān)系,特別當(dāng)系統(tǒng)的處理能力成某種變化趨勢(shì)時(shí),要考慮到系統(tǒng)GC對(duì)系統(tǒng)性能的影響,為了找到兩者的關(guān)系,可以通過采樣,圖形比較來進(jìn)行分析。如果發(fā)現(xiàn)二者之間有密切的聯(lián)系,可以考慮對(duì)JVM的配置進(jìn)行優(yōu)化,比如:對(duì)最優(yōu)化堆的大小進(jìn)行調(diào)整。
對(duì)于不同的應(yīng)用程序,最優(yōu)化堆大小的設(shè)置都有可能不同。如果堆設(shè)置較大,可能導(dǎo)致GC的次數(shù)變少,但每次GC所花的時(shí)間很長(zhǎng),從而導(dǎo)致系統(tǒng)的處理能力抖動(dòng)很大。此外如果堆設(shè)置過大,會(huì)占用過多的內(nèi)存,使內(nèi)存資源耗盡,從而會(huì)頻繁的進(jìn)行IO操作來使用虛擬內(nèi)存。 如果堆設(shè)置較小,可能導(dǎo)致GC變的頻繁,但每次GC所花的時(shí)間不會(huì)太長(zhǎng),每次GC對(duì)系統(tǒng)的性能影響相對(duì)也會(huì)小些。但是如果堆設(shè)置過小, 會(huì)使得對(duì)象可分配空間變小,從而會(huì)頻繁的GC來釋放內(nèi)存空間,而每次GC,都會(huì)耗用一定的系統(tǒng)資源。因此,要通過試驗(yàn)和監(jiān)控?cái)?shù)據(jù),設(shè)法使的我們所設(shè)置的堆大小能夠使得系統(tǒng)運(yùn)行最優(yōu)化。
- Brian Goetz (brian@quiotix.com), Principal Consultant, Quiotix Corp Java theory and practice: Garbage collection in the HotSpot JVM http://www-128.ibm.com/developerworks/java/library/j-jtp11253/
- Clive Kates , kates@uk.ibm.com WebSphere Application Server IBM JVM tuning guide
- WebSphere Application Server,Information Center http://publib.boulder.ibm.com/infocenter/wasinfo/v6r0/index.jsp
- Jack Shirazi , Kirk Pepperdine Eye on performance: Tuning garbage collection in the HotSpot JVM http://www-128.ibm.com/developerworks/java/library/j-perf06304/
- IBM alphaWorks' GCCollector, http://www.alphaworks.ibm.com/tech/gcdiag?open&S_TACT=105AGX59&S_CMP=GR&ca=dgr-jw26awgcdiag
![]() |
||
|
![]() |
李曉華, IBM CDL ODSC部門工程師,參與了中國(guó)電信3G大型項(xiàng)目. 寫作經(jīng)歷:曾在國(guó)家核心期刊《計(jì)算機(jī)集成制造》(CIMS)發(fā)表文章:《面向異構(gòu)過程庫的過程搜索方法與系統(tǒng)研究》。Email: xiaohual@cn.ibm.com |