szhswl
          宋針還的個(gè)人空間
          在中間件應(yīng)用服務(wù)器的整體調(diào)優(yōu)中,有關(guān)于等待隊(duì)列、執(zhí)行線程,EJB池以及數(shù)據(jù)庫連接池和Statement Cache方面的調(diào)優(yōu),這些都屬于系統(tǒng)參數(shù)方面的調(diào)優(yōu),本文主要從另外一個(gè)角度,也就是從應(yīng)用的角度來解決中間件應(yīng)用服務(wù)器的內(nèi)存泄露問題,從這個(gè)角度來提高系統(tǒng)的穩(wěn)定性和性能。

          項(xiàng)目背景

          問題描述

          某個(gè)大型項(xiàng)目(Use Case用例超過300個(gè)),在項(xiàng)目上線后,其Web應(yīng)用服務(wù)器經(jīng)常宕機(jī)。表現(xiàn)為:

          1. 應(yīng)用服務(wù)器內(nèi)存長期不合理占用,內(nèi)存經(jīng)常處于高位占用,很難回收到低位;

          2. 應(yīng)用服務(wù)器極為不穩(wěn)定,幾乎每兩天重新啟動一次,有時(shí)甚至每天重新啟動一次;

          3. 應(yīng)用服務(wù)器經(jīng)常做Full GC(Garbage Collection),而且時(shí)間很長,大約需要30-40秒,應(yīng)用服務(wù)器在做Full GC的時(shí)候是不響應(yīng)客戶的交易請求的,非常影響系統(tǒng)性能。

          Web應(yīng)用服務(wù)器的物理部署

                一臺Unix服務(wù)器(4CPU,8G Memory)來部署本W(wǎng)eb應(yīng)用程序;Web應(yīng)用程序部署在中間件應(yīng)用服務(wù)器上;部署了一個(gè)節(jié)點(diǎn)(Node),只配置一個(gè)應(yīng)用服務(wù)器實(shí)例(Instance),沒有做Cluster部署。

          Web應(yīng)用服務(wù)器啟動腳本中的內(nèi)存參數(shù)
          MEM_ARGS="-XX:MaxPermSize=128m -XX:MaxNewSize=512m -Xms3096m
          -Xmx3096m -XX:+Printetails -Xloggc:./inwebapp1/gc.$$"

          可以看出目前生產(chǎn)系統(tǒng)中Web應(yīng)用服務(wù)器的內(nèi)存分配為3G Memory。

          Web應(yīng)用服務(wù)器的重要部署參數(shù)
          參數(shù)名稱 參數(shù)值 參數(shù)解釋
          kernel.default(Thread Count) 120 執(zhí)行線程數(shù)目,是并發(fā)處理能力的重要參數(shù)
          Session Timeout 240分鐘(4小時(shí)) HttpSession會話超時(shí)

           

          分析

          分析方法

          內(nèi)存長期占用并導(dǎo)致系統(tǒng)不穩(wěn)定一般有兩種可能:

          1. 對象被大量創(chuàng)建而且被緩存,在舊的對象釋放前又有大量新的對象被創(chuàng)建使得內(nèi)存長期高位占用。

          • 表現(xiàn)為:內(nèi)存不斷被消耗、在高位時(shí)也很難回歸到低位,有大量的對象在不斷的創(chuàng)建,經(jīng)過很長時(shí)間后又被回收。例如:在HttpSession中保存了大量的分頁查詢數(shù)據(jù),而HttpSession的會話超時(shí)時(shí)間設(shè)置過長(例如:1天),那么在舊的對象釋放前又有大量新的對象在第二天產(chǎn)生。
          • 解決辦法:對共享的對象可以采用池機(jī)制進(jìn)行緩存,避免各自創(chuàng)建;緩存的臨時(shí)對象應(yīng)該及時(shí)釋放;另一種辦法是擴(kuò)大系統(tǒng)的內(nèi)存容量。

          2. 另一種情況就是內(nèi)存泄漏問題

          • 表現(xiàn)為:內(nèi)存回收低位點(diǎn)不斷升高(以每次內(nèi)存回收的最低點(diǎn)連成一條直線,那么它是一條上升線);內(nèi)存回收的頻率也越來越高,內(nèi)存占用也越來越高,最終出現(xiàn)"Out of Memory Exception"的系統(tǒng)異常。
          • 解決辦法:定位那些有內(nèi)存泄漏的類或?qū)ο蟛⑿薷耐晟七@些類以避免內(nèi)存泄漏。方法是:經(jīng)過一段時(shí)間的測試、監(jiān)控,如果某個(gè)類的對象數(shù)目屢創(chuàng)新高,即使在JVM Full GC后仍然數(shù)目降不下來,這些對象基本上是屬于內(nèi)存泄漏的對象了。

          問題定位

          這里請看5月份 Web應(yīng)用服務(wù)器的內(nèi)存回收圖形:

          《注意:5月18日早上10點(diǎn)重新啟動了Web服務(wù)器,5月20日早上又重新啟動了Web服務(wù)器。》

          • 在Web應(yīng)用重要部署參數(shù)中,我們知道:Session的超時(shí)時(shí)間為4個(gè)小時(shí),我們在監(jiān)控平臺也觀測到:在18日晚上10點(diǎn)左右所有的會話都過期了,從圖形一中也能看出18日晚上確實(shí)系統(tǒng)的內(nèi)存有回收到40%(就象股票的高位跳水);
          • 從圖形一(5月18日)中我們也能看到Full GC回收后的內(nèi)存占用率走勢(紅色曲線),上午基本平滑上升到20%(內(nèi)存占用率),中午開始上升到30%,下午上升到40%
          • 從圖形二(5月19日)中我們也能看到Full GC回收后的內(nèi)存占用率走勢(紅色曲線),上午又上升到了60%,到下午上升到了70%。
          • 從黃色曲線(GC花費(fèi)的時(shí)間,以秒為單位),F(xiàn)ull GC的頻率也在增快,時(shí)間耗費(fèi)也越來越長,在圖形一中基本高位在20秒左右,到19日基本都是30-40秒之間了。

           圖形一 5月18日
          圖形一 5月18日

          圖二
          圖二

          通過上述分析,我們基本定位到了Web應(yīng)用服務(wù)器的內(nèi)存在高位長期占用的原因了:是內(nèi)存泄露!并且正是由于這個(gè)原因?qū)е孪到y(tǒng)不穩(wěn)定、響應(yīng)客戶請求越來越慢的。

          解決方法

          方法如下:

          • 我們從圖形二中發(fā)現(xiàn),在8.95(將近9點(diǎn)鐘)到9.66(將近9點(diǎn)40)期間有幾次Full GC,但是有內(nèi)存泄漏,從占用率40%上升到50%左右,泄漏了大約10%的內(nèi)存,約300M;
          • 我們在自己搭建的Web應(yīng)用服務(wù)器平臺(應(yīng)用軟件版本和生產(chǎn)版本一致)做這一階段相同的查詢交易;表明對同一個(gè)黑盒(Web應(yīng)用)施加同樣的刺激(相同的操作過程和查詢交易)以期重現(xiàn)現(xiàn)象;
          • 我們使用Jprofiler工具對Web應(yīng)用服務(wù)器的內(nèi)存進(jìn)行實(shí)時(shí)監(jiān)控;
          • 做完這些交易后,用戶退出系統(tǒng),并等待Web應(yīng)用服務(wù)器的HttpSession超時(shí)(我們這里設(shè)置為15分鐘);
          • 我們對Web應(yīng)用服務(wù)器做了兩次強(qiáng)制性的內(nèi)存回收操作。

          發(fā)現(xiàn)如下:


          圖三
          圖三

          如圖三所示,內(nèi)存經(jīng)過HttpSession超時(shí)后,并強(qiáng)制gc后,仍然有大量的對象沒有釋放。例如:gov.gdlt.taxcore.comm.security.MenuNode,仍然有807個(gè)實(shí)例沒有釋放。

          我們繼續(xù)追溯發(fā)現(xiàn),這些MenuNode首先存放在一個(gè)ArrayList對象中,然后發(fā)現(xiàn)這個(gè)ArrayList對象又是存放在WHsessionAttrVO對象的Map中,WHsessionAttrVO 對象又是存放在ExternalSessionManager的staic Map中(名稱為sessionMap),如圖四所示。


          圖四
          圖四

          我們發(fā)現(xiàn)gov.gdlt.taxcore.taxevent.xtgl.comm.WHsessionAttrVO中保存了EJBSessionId信息(登錄用戶的唯一標(biāo)志,由用戶id+登錄時(shí)間戳組成,每天都不同)和一個(gè)HashMap,這個(gè)HashMap中的內(nèi)容有:

          • ArrayList: 內(nèi)有MenuTreeNodes(菜單樹節(jié)點(diǎn))
          • HashMap: 內(nèi)有操作人員代碼信息
          • CurrentVersion:當(dāng)前版本號
          • CurrentTime:當(dāng)前系統(tǒng)時(shí)間

          WHsessionAttrVO這個(gè)對象的最終存放在ExternalSessionManager的static Map sessionMap中,由于ExternalSessionManager是一個(gè)全局的單實(shí)例,不會釋放,所以它的成員變量sessionMap中的數(shù)據(jù)也不會釋放,而Map中的Key值為EJBSessionId,每天登錄的用戶EJBSessionId都不同,就造成了每天的登錄信息(包括菜單信息)都保存在sessionMap中不會被釋放,最終造成了內(nèi)存的泄漏。


          圖五
          圖五

          如上圖所示:WHsessionAttrsVO對象中除了有一個(gè)String對象(內(nèi)容是EJBSessionId),還有一個(gè)HashMap對象。


          圖六
          圖六

          如上圖所示,這個(gè)HashMap中的內(nèi)容主要有menuTreeNodes為key,value為ArrayList的對象和以czrydminfo為key,value為HashMap對象的數(shù)據(jù)。


          圖七
          圖七

          如上圖所示:menuTreeNodes為key,value為ArrayList對象中包含的對象有許多的MenuNode對象,封裝的都是用戶的菜單節(jié)點(diǎn)。


          圖八
          圖八

          如上圖所示,最頂層(Root)的初始對象為一個(gè)ExternalSessionManager對象,其中的一個(gè)成員變量為static (靜態(tài)的),名稱為:sessionMap,這個(gè)對象是singleton方式的,全局只有一個(gè)。

          初步估量

          我們從圖形一和圖形二中可以看出,每天應(yīng)用服務(wù)器損失大約40%的內(nèi)存,大約1G左右。

          從圖形四可以看出,當(dāng)前用戶(Id=24400001129)有807個(gè)菜單項(xiàng)(每個(gè)菜單項(xiàng)為一個(gè)MenuNode 對象實(shí)例,圖形四中的這個(gè)實(shí)例的size為592 Byte),這些菜單數(shù)據(jù)和用戶基本登錄信息(czrydmInfo HashMap)也都存放在WHsessionAttrVO對象中,當(dāng)前這個(gè)WHsessionAttrVO對象的size為457K。

          我們做如下估算:

          假設(shè)平均每天有4千人(估計(jì)值,這個(gè)數(shù)值僅僅是5月19日峰值的1/2左右)登錄系統(tǒng)(有重復(fù)登錄的現(xiàn)象,例如:上午登錄一次,中午退出系統(tǒng),下午登錄一次),以平均每人占用200K(估計(jì)值,是用戶id=24400001129 的Size的1/2左右)來計(jì)算,一天泄漏的內(nèi)存約800M,比較符合目前內(nèi)存泄漏的情況。當(dāng)然,這種估計(jì)仍然需要經(jīng)過實(shí)踐的檢驗(yàn),方法是:當(dāng)這次發(fā)現(xiàn)的內(nèi)存泄漏問題解決后看系統(tǒng)是否還有其它內(nèi)存泄漏問題。


          方案

          ExternalSessionManager類是當(dāng)初某某軟件商設(shè)計(jì)的用來解決Web服務(wù)器負(fù)載均衡的模塊,這個(gè)類主要用來保存客戶的基本登錄信息(包括會話的EJBSessionId),以維護(hù)多個(gè)Web服務(wù)器之間的會話信息一致。

          改進(jìn)方案有兩種:

          • 從架構(gòu)設(shè)計(jì)方面改進(jìn)

            實(shí)現(xiàn)Web層的負(fù)載均衡有很多標(biāo)準(zhǔn)的實(shí)現(xiàn)方式。例如:采用負(fù)載均衡設(shè)備(硬件或軟件)來實(shí)現(xiàn)。

            如果采用新的Web層的負(fù)載均衡方式,那么就可以去掉ExternalSessionManager這個(gè)類了。

          • 從應(yīng)用實(shí)現(xiàn)方面改進(jìn)

            保留當(dāng)前的Web層的負(fù)載均衡設(shè)計(jì)機(jī)制,僅僅從應(yīng)用實(shí)現(xiàn)方面解決內(nèi)存泄漏問題,首先菜單信息不應(yīng)該保存在ExternalSessionManager中。其次,增加對ExternalSessionManager類中用戶會話登錄信息的清除,有幾種方式可以選擇:

            • 被動方式,當(dāng)HttpSession會話超時(shí)(或過期)被Web應(yīng)用服務(wù)器回收時(shí)清除相應(yīng)的ExternalSessionManager中的過期會話登錄信息。
            • 主動方式,可以采用任務(wù)定時(shí)清理每天的過期會話登錄信息或線程輪詢清理。
            • 采用新的會話登錄信息存儲方式,ExternalSessionManager的sessionMap中的key值不再以EJBSessionId作為鍵值,而是以用戶id(EJBSessionId的前11位)代替。由于用戶id每天都是一樣的,所以不會造成內(nèi)存泄漏。保存得登錄信息也不再包含菜單節(jié)點(diǎn)信息,而只是登錄基本信息。最多也只是保存整個(gè)系統(tǒng)所有的用戶id及其基本登錄信息(大約每個(gè)用戶的登錄信息只有1.5K左右,而目前這個(gè)系統(tǒng)的營業(yè)網(wǎng)點(diǎn)用戶為1萬左右,所以大約只占用Web服務(wù)器15M內(nèi)存)。


          實(shí)施情況

          采用的方案:某某軟件商采用了新的會話登錄信息存貯方案,即:ExternalSessionManager的成員變量sessionMap中不再保存用戶菜單信息,只保存基本的登錄信息;存儲方式采用用戶id(11位)作為鍵值(key)來保留用戶基本登錄信息。

          基本分析:由于基本登錄信息只有1K左右,而目前內(nèi)網(wǎng)登錄的用戶總數(shù)也只有8887個(gè),所以只保存了大約10M-15M的信息在內(nèi)存,占用量很小,并且不會有內(nèi)存泄漏。用戶菜單信息保存在session中,如果用戶退出時(shí)點(diǎn)擊logout頁面,那么應(yīng)用服務(wù)器可以很快地釋放這部分內(nèi)存;如果用戶直接關(guān)閉窗口,那么保存在session中的菜單信息只有等會話超時(shí)后才會由系統(tǒng)清除并回收內(nèi)存。

          監(jiān)控狀況:


          圖九
          圖九

          如圖九所示,ExternalSessionManager中只保留了簡單的登錄信息(Map中保存了WHsessionAttrVO對象),包括:當(dāng)前版本(currentversion),操作人員代碼基本信息(czrydmInfo),當(dāng)前時(shí)間(currenttime)。


          圖十
          圖十

          如圖十所示,這個(gè)登錄用戶的基本信息只有1368 bytes,大約1.3K


          圖十一
          圖十一

          如圖十一所示,一共有兩個(gè)用戶(相同的用戶id)登錄系統(tǒng),當(dāng)一個(gè)用戶使用logout頁面退出時(shí),保留在session中的菜單信息(MenuNode)立刻釋放了,所以Difference一欄減少了806個(gè)菜單項(xiàng)。


          圖十二
          圖十二

          如圖十二所示,當(dāng)另外一個(gè)會話超時(shí)后,應(yīng)用服務(wù)器回收了整個(gè)會話的菜單信息(MenuNode),圖上已經(jīng)沒有MenuNode對象了。并且由于是同一個(gè)用戶登錄,所以保留在ExternalSessionManager成員變量sessionMap中的對象WHsessionAttrVO只有一個(gè)(id=24400001129),而沒有產(chǎn)生多個(gè),沒有因?yàn)槎啻蔚卿浂a(chǎn)生多個(gè)對象的后果,避免了內(nèi)存泄漏問題的出現(xiàn),解決了前期定位的內(nèi)存泄漏問題。


          圖十三
          圖十三

          如圖十三所示,經(jīng)過gc內(nèi)存回收后,發(fā)現(xiàn)內(nèi)存回收比較穩(wěn)定,基本都回收到了最低點(diǎn),也證明了內(nèi)存沒有泄露。

          結(jié)論與建議:從測試情況看,解決了前期定位的內(nèi)存泄漏問題。

          生產(chǎn)系統(tǒng)實(shí)施后的監(jiān)控與分析

          經(jīng)過調(diào)優(yōu)后,我們發(fā)現(xiàn):在2005年6月2日晚9點(diǎn)40左右重新部署、啟動了Web應(yīng)用服務(wù)器(采用了新的調(diào)優(yōu)方案)。經(jīng)過幾天的監(jiān)控運(yùn)行,發(fā)現(xiàn)Web應(yīng)用服務(wù)器目前運(yùn)行基本穩(wěn)定,目前沒有出現(xiàn)新的內(nèi)存泄漏問題,下列圖示說明了這一點(diǎn)


          圖十四 2005年6月2日
          圖十四 2005年6月2日

          如圖十四所示,6月2日晚21.7(21點(diǎn)42分)重新啟動應(yīng)用服務(wù)器,內(nèi)存占用很少,大約為15%(請看紅色曲線),每次GC消耗的時(shí)間也很短,大約在5秒以內(nèi)(請看黃色曲線)。


          圖十五 2005年6月3日周五
          圖十五 2005年6月3日周五

          如圖十五所示,在6月3日周五的整個(gè)工作日內(nèi),內(nèi)存的回收基本到位,回收位置控制在20%-30%之間,也就是在600M-900M之間(請看紅色曲線的最低點(diǎn)),始終可以回收2G的內(nèi)存供應(yīng)用程序使用,每次GC的時(shí)間最高不超過20秒,F(xiàn)ull GC平均在10秒左右,時(shí)間消耗比較短(請看黃色曲線)。


          圖十六2005年6月5日周日
          圖十六2005年6月5日周日

          如圖十六所示,在周日休息日期間,Web應(yīng)用服務(wù)器全天只做了大約4次Full GC(黃色曲線中的小山峰),時(shí)間都在10秒以內(nèi);大的Full GC后,內(nèi)存只占用10%,內(nèi)存回收很徹底。


          圖十七 2005年6月6日周一
          圖十七 2005年6月6日周一

          如圖十七所示,在周一工作日期間,內(nèi)存回收還是不錯(cuò)的,基本可以回收到30%(見紅色曲線的最低點(diǎn)),即:占用900M內(nèi)存空間,剩余2G的內(nèi)存空間;Full GC的時(shí)間大部分控制在20秒以內(nèi),平均15秒(見黃色曲線)。


          圖十八 2005年6月7日周二
          圖十八 2005年6月7日周二

          如圖十八所示,在6月7日周二早上,大約8:30左右,Web應(yīng)用服務(wù)器作了一次Full GC,用了10秒的時(shí)間,把內(nèi)存回收到了10%的位置,為后續(xù)的使用騰出了90%的內(nèi)存空間。內(nèi)存回收仍然比較徹底,說明基本沒有內(nèi)存泄漏問題。

          經(jīng)過這幾天的監(jiān)控分析,我們可以看出:

          • Web應(yīng)用服務(wù)器的內(nèi)存使用已經(jīng)比較合理,內(nèi)存在工作日的占用在20%至30%之間,約1G的內(nèi)存占用,有2G的內(nèi)存空間富裕;而在空閑時(shí)間(周日,每天的凌晨等)內(nèi)存可以回收到10%,有90%的內(nèi)存空間富裕;
          • Web應(yīng)用服務(wù)器的Full GC的次數(shù)明顯減少了并且每次Full GC占用的時(shí)間也很少,基本控制在10-20秒之間,有的甚至在10秒以內(nèi),明顯改善了內(nèi)網(wǎng)應(yīng)用服務(wù)器內(nèi)存的使用;
          • 從6月2日重新部署之后,Web應(yīng)用服務(wù)器沒有出現(xiàn)宕機(jī)重啟的現(xiàn)象。


          總結(jié)

           通過本文,我們可以看到,內(nèi)存的泄露將會導(dǎo)致服務(wù)器的宕機(jī),系統(tǒng)性能就更別說了。對于系統(tǒng)內(nèi)存泄露問題應(yīng)該從服務(wù)器GC日志方面進(jìn)行早診斷,使用工具早確認(rèn)并提出解決方案,排除內(nèi)存泄露問題,提高系統(tǒng)性能,以規(guī)避項(xiàng)目風(fēng)險(xiǎn)。

          本文轉(zhuǎn)自:http://yufeimen.javaeye.com/blog/70721



          ---------------------------------------------------------------------------------------------------------------------------------
          說人之短,乃護(hù)己之短。夸己之長,乃忌人之長。皆由存心不厚,識量太狹耳。能去此弊,可以進(jìn)德,可以遠(yuǎn)怨。
          http://www.aygfsteel.com/szhswl
          ------------------------------------------------------------------------------------------------------ ----------------- ---------
          posted on 2008-01-07 12:03 宋針還 閱讀(578) 評論(0)  編輯  收藏 所屬分類: 開發(fā)測試工具
          主站蜘蛛池模板: 和平县| 阜南县| 湄潭县| 封丘县| 山西省| 建瓯市| 台州市| 阜平县| 彭水| 佛坪县| 南部县| 博爱县| 军事| 益阳市| 阳谷县| 桃园县| 上虞市| 双桥区| 广河县| 名山县| 满城县| 普陀区| 台北市| 河曲县| 新巴尔虎右旗| 新民市| 福清市| 兴国县| 钦州市| 定日县| 盐亭县| 大渡口区| 仲巴县| 专栏| 社会| 贡嘎县| 沂水县| 五大连池市| 长沙市| 巴林右旗| 榆树市|