一、內存溢出的定義和原因
定義
內(nei)(nei)(nei)存(cun)溢出(chu)(chu)是(shi)指應用(yong)(yong)系統中(zhong)存(cun)在無法(fa)回收(shou)(shou)的(de)(de)(de)(de)內(nei)(nei)(nei)存(cun)或(huo)使用(yong)(yong)的(de)(de)(de)(de)內(nei)(nei)(nei)存(cun)過多,最終(zhong)使得程(cheng)序(xu)運(yun)行要(yao)用(yong)(yong)到的(de)(de)(de)(de)內(nei)(nei)(nei)存(cun)大(da)于虛(xu)擬機能提供的(de)(de)(de)(de)最大(da)內(nei)(nei)(nei)存(cun)。為了解決Java中(zhong)內(nei)(nei)(nei)存(cun)溢出(chu)(chu)問題,我們首先必須(xu)了解Java是(shi)如何管理內(nei)(nei)(nei)存(cun)的(de)(de)(de)(de)。Java的(de)(de)(de)(de)內(nei)(nei)(nei)存(cun)管理就(jiu)是(shi)對象(xiang)的(de)(de)(de)(de)分配和(he)釋(shi)放(fang)(fang)問題。在Java中(zhong),內(nei)(nei)(nei)存(cun)的(de)(de)(de)(de)分配是(shi)由(you)程(cheng)序(xu)完成的(de)(de)(de)(de),而內(nei)(nei)(nei)存(cun)的(de)(de)(de)(de)釋(shi)放(fang)(fang)是(shi)由(you)垃(la)圾收(shou)(shou)集器(GarbageCollection,GC)完成的(de)(de)(de)(de),程(cheng)序(xu)員不(bu)(bu)需要(yao)通過調(diao)用(yong)(yong)GC函(han)數(shu)來釋(shi)放(fang)(fang)內(nei)(nei)(nei)存(cun),因為不(bu)(bu)同(tong)的(de)(de)(de)(de)JVM實現者可能使用(yong)(yong)不(bu)(bu)同(tong)的(de)(de)(de)(de)算法(fa)管理GC,有(you)的(de)(de)(de)(de)是(shi)內(nei)(nei)(nei)存(cun)使用(yong)(yong)到達一定程(cheng)度時,GC才開(kai)始工作(zuo),也有(you)定時執行的(de)(de)(de)(de),有(you)的(de)(de)(de)(de)是(shi)中(zhong)斷式執行GC。但(dan)GC只能回收(shou)(shou)無用(yong)(yong)并(bing)且不(bu)(bu)再(zai)被其它對象(xiang)引用(yong)(yong)的(de)(de)(de)(de)那些對象(xiang)所占用(yong)(yong)的(de)(de)(de)(de)空間。Java的(de)(de)(de)(de)內(nei)(nei)(nei)存(cun)垃(la)圾回收(shou)(shou)機制(zhi)是(shi)從程(cheng)序(xu)的(de)(de)(de)(de)主要(yao)運(yun)行對象(xiang)開(kai)始檢查引用(yong)(yong)鏈(lian),當遍歷一遍后發現沒有(you)被引用(yong)(yong)的(de)(de)(de)(de)孤立(li)對象(xiang)就(jiu)作(zuo)為垃(la)圾回收(shou)(shou)。
原因
1、內存中加載的數據量過于(yu)龐大,如(ru)一(yi)次從數據庫取出過多數據。
2、集(ji)合類(lei)中有對對象的引用,使(shi)用完后未清空,使(shi)得JVM不能(neng)回(hui)收。
3、代碼(ma)中存在死(si)循(xun)環(huan)或循(xun)環(huan)產生過(guo)多重復的對象實體。
4、使(shi)用(yong)的(de)第(di)三(san)方(fang)軟件(jian)中的(de)BUG。
5、啟動參(can)數設定(ding)的過(guo)小。
二、內存溢出的解決問題
第(di)一(yi)步,就是(shi)修(xiu)改JVM啟(qi)(qi)動參(can)數(shu),直接增加(jia)內(nei)存(cun)(cun)。這一(yi)點(dian)看上去似乎(hu)很(hen)簡單,但很(hen)容易(yi)被忽(hu)略。JVM默認可以(yi)使用(yong)的(de)(de)內(nei)存(cun)(cun)為(wei)64M,Tomcat默認可以(yi)使用(yong)的(de)(de)內(nei)存(cun)(cun)為(wei)128MB,對于稍復雜一(yi)點(dian)的(de)(de)系統就會不夠(gou)用(yong)。在某項目中,就因為(wei)啟(qi)(qi)動參(can)數(shu)使用(yong)的(de)(de)默認值,經常報“OutOfMemory”錯誤。因此,-Xms,-Xmx參(can)數(shu)一(yi)定不要(yao)忘(wang)記(ji)加(jia)。
第二步,檢查錯(cuo)(cuo)(cuo)誤(wu)日志,查看“OutOfMemory”錯(cuo)(cuo)(cuo)誤(wu)前是(shi)否有(you)其它異常(chang)或(huo)錯(cuo)(cuo)(cuo)誤(wu)。在一(yi)個項(xiang)目(mu)中,使(shi)(shi)用(yong)兩個數(shu)據庫(ku)連(lian)(lian)接(jie)(jie)(jie),其中專用(yong)于發送短信(xin)的(de)數(shu)據庫(ku)連(lian)(lian)接(jie)(jie)(jie)使(shi)(shi)用(yong)DBCP連(lian)(lian)接(jie)(jie)(jie)池管(guan)理,用(yong)戶為不將短信(xin)發出,有(you)意(yi)將數(shu)據庫(ku)連(lian)(lian)接(jie)(jie)(jie)用(yong)戶名(ming)改錯(cuo)(cuo)(cuo),使(shi)(shi)得日志中有(you)許(xu)多數(shu)據庫(ku)連(lian)(lian)接(jie)(jie)(jie)異常(chang)的(de)日志,一(yi)段時間后,就出現(xian)“OutOfMemory”錯(cuo)(cuo)(cuo)誤(wu)。經分析(xi),這是(shi)由于DBCP連(lian)(lian)接(jie)(jie)(jie)池BUG引起的(de),數(shu)據庫(ku)連(lian)(lian)接(jie)(jie)(jie)不上后,沒(mei)有(you)將連(lian)(lian)接(jie)(jie)(jie)釋(shi)放,最終使(shi)(shi)得DBCP報“OutOfMemory”錯(cuo)(cuo)(cuo)誤(wu)。經過修(xiu)改正確數(shu)據庫(ku)連(lian)(lian)接(jie)(jie)(jie)參數(shu)后,就沒(mei)有(you)再出現(xian)內存(cun)溢出的(de)錯(cuo)(cuo)(cuo)誤(wu)。
查看日(ri)(ri)志(zhi)(zhi)對于(yu)分(fen)析內存溢(yi)出是非(fei)常(chang)重要的,通過仔細查看日(ri)(ri)志(zhi)(zhi),分(fen)析內存溢(yi)出前(qian)做過哪些操作(zuo),可以大致定位有問題的模塊(kuai)。
第三(san)步,安排(pai)有經驗(yan)的(de)編(bian)程人員對代(dai)碼進行走查(cha)和分析,找出可能(neng)發生內存(cun)溢(yi)出的(de)位置。重(zhong)點排(pai)查(cha)以下幾點:
1、檢查代碼(ma)中(zhong)是否有死循環或遞(di)歸調(diao)用。
2、檢查是否有大循環重復產生新對象實體。
3、檢查對數據(ju)(ju)(ju)庫(ku)(ku)(ku)查詢(xun)中(zhong),是否有(you)(you)一次(ci)獲得全部數據(ju)(ju)(ju)的查詢(xun)。一般(ban)來說,如果一次(ci)取十(shi)萬條記錄到內存(cun),就可能引起(qi)內存(cun)溢出(chu)。這(zhe)個問題比較隱蔽,在上線(xian)前,數據(ju)(ju)(ju)庫(ku)(ku)(ku)中(zhong)數據(ju)(ju)(ju)較少,不容易出(chu)問題,上線(xian)后,數據(ju)(ju)(ju)庫(ku)(ku)(ku)中(zhong)數據(ju)(ju)(ju)多了(le),一次(ci)查詢(xun)就有(you)(you)可能引起(qi)內存(cun)溢出(chu)。因此對于數據(ju)(ju)(ju)庫(ku)(ku)(ku)查詢(xun)盡量采用分頁的方式查詢(xun)。
4、檢查List、MAP等(deng)集(ji)合對(dui)象(xiang)(xiang)(xiang)是否有使用(yong)完后,未清(qing)除的(de)問題。List、MAP等(deng)集(ji)合對(dui)象(xiang)(xiang)(xiang)會始(shi)終存(cun)有對(dui)對(dui)象(xiang)(xiang)(xiang)的(de)引用(yong),使得這些對(dui)象(xiang)(xiang)(xiang)不能被(bei)GC回(hui)收。
第四步,使用(yong)內存(cun)(cun)查(cha)看工具動態查(cha)看內存(cun)(cun)使用(yong)情況。某個項目上(shang)線后,每次(ci)系統啟(qi)動兩天后,就(jiu)會出(chu)現內存(cun)(cun)溢出(chu)的(de)錯誤。這種情況一般是代碼中出(chu)現了緩慢的(de)內存(cun)(cun)泄漏,用(yong)上(shang)面(mian)三個步驟解決不了,這就(jiu)需要使用(yong)內存(cun)(cun)查(cha)看工具了。
內存查看工具有許多,比較有名的有:Optimizeit Profiler、JProbeProfiler、JinSight和Java1.5的Jconsole等。它們的基本工作原理大同小異,都是監測Java程序運行時所有對象的申請、釋放等動作,將內存管理的所有信息進行(xing)統計、分析、可(ke)視(shi)化。開發(fa)人員可(ke)以根據這些信息判(pan)斷程序是(shi)否有內(nei)(nei)(nei)存(cun)(cun)泄(xie)(xie)漏(lou)問(wen)題(ti)。一(yi)般來說(shuo),一(yi)個正常(chang)的(de)(de)(de)(de)系(xi)統在其(qi)啟動完成后其(qi)內(nei)(nei)(nei)存(cun)(cun)的(de)(de)(de)(de)占用(yong)量是(shi)基本穩定的(de)(de)(de)(de),而不(bu)應該是(shi)無(wu)限制的(de)(de)(de)(de)增(zeng)(zeng)長的(de)(de)(de)(de)。持(chi)續地(di)觀察系(xi)統運(yun)行(xing)時(shi)(shi)使(shi)(shi)用(yong)的(de)(de)(de)(de)內(nei)(nei)(nei)存(cun)(cun)的(de)(de)(de)(de)大(da)小,可(ke)以看(kan)到在內(nei)(nei)(nei)存(cun)(cun)使(shi)(shi)用(yong)監控窗(chuang)口中(zhong)是(shi)基本規(gui)則(ze)(ze)的(de)(de)(de)(de)鋸齒形的(de)(de)(de)(de)圖線,如果內(nei)(nei)(nei)存(cun)(cun)的(de)(de)(de)(de)大(da)小持(chi)續地(di)增(zeng)(zeng)長,則(ze)(ze)說(shuo)明系(xi)統存(cun)(cun)在內(nei)(nei)(nei)存(cun)(cun)泄(xie)(xie)漏(lou)問(wen)題(ti)。通過(guo)間隔(ge)一(yi)段時(shi)(shi)間取一(yi)次內(nei)(nei)(nei)存(cun)(cun)快(kuai)(kuai)照,然后對(dui)(dui)內(nei)(nei)(nei)存(cun)(cun)快(kuai)(kuai)照中(zhong)對(dui)(dui)象(xiang)的(de)(de)(de)(de)使(shi)(shi)用(yong)與(yu)(yu)引(yin)用(yong)等信息進行(xing)比(bi)對(dui)(dui)與(yu)(yu)分析,可(ke)以找出是(shi)哪(na)個類的(de)(de)(de)(de)對(dui)(dui)象(xiang)在泄(xie)(xie)漏(lou)。
通過以(yi)上四個步驟(zou)的(de)分析與處(chu)理,基本能處(chu)理內(nei)存溢(yi)出的(de)問題。當(dang)然(ran),在這些(xie)過程中也(ye)需要相當(dang)的(de)經驗與敏感(gan)度,需要在實際的(de)開發與調試過程中不斷(duan)積累。
申明:以上方法源于程序系統索引或網民分享提供,僅供您參考使用,不代表本網站的研究觀點,證明有效,請注意甄別內容來源的真實性和權威性。