内存溢出

内存溢出

計算機專業術語
内存溢出(out of memory)通俗理解就是内存不夠,通常在運行大型軟件或遊戲時,軟件或遊戲所需要的内存遠遠超出了你主機内安裝的内存所承受大小,就叫内存溢出。此時軟件或遊戲就運行不了,系統會提示内存溢出,有時候會自動關閉軟件,重啟電腦或者軟件後釋放掉一部分内存又可以正常運行該軟件或遊戲一段時間。[1]
    中文名:内存溢出 外文名:out of memory 适用領域: 所屬學科: 解決辦法:關閉軟件,重啟電腦或者軟件 行業:計算機

基本含義

内存溢出已經是軟件開發曆史上存在了近40年的“老大難”問題,像在“紅色代碼”病毒事件中表現的那樣,它已經成為黑客攻擊企業網絡的“罪魁禍首”。如在一個域中輸入的數據超過了它的要求就會引發數據溢出問題,多餘的數據就可以作為指令在計算機上運行。據有關安全小組稱,操作系統中超過50%的安全漏洞都是由内存溢出引起的,其中大多數與微軟的技術有關。

基本内容

為了便于理解,我們不妨打個比方。緩沖區溢出好比是将十磅的糖放進一個隻能裝五磅的容器裡。一旦該容器放滿了,餘下的部分就溢出在櫃台和地闆上,弄得一團糟。由于計算機程序的編寫者寫了一些編碼,但是這些編碼沒有對目的區域或緩沖區——五磅的容器——做适當的檢查,看它們是否夠大,能否完全裝入新的内容——十磅的糖,結果可能造成緩沖區溢出的産生。如果打算被放進新地方的數據不适合,溢得到處都是,該數據也會制造很多麻煩。但是,如果緩沖區僅僅溢出,這隻是一個問題。到此時為止,它還沒有破壞性。當糖溢出時,櫃台被蓋住。可以把糖擦掉或用吸塵器吸走,還櫃台本來面貌。與之相對的是,當緩沖區溢出時,過剩的信息複蓋的是計算機内存中以前的内容。除非這些被複蓋的内容被保存或能夠恢複,否則就會永遠丢失。

在丢失的信息裡有能夠被程序調用的子程序的列表信息,直到緩沖區溢出發生。另外,給那些子程序的信息——參數——也丢失了。這意味着程序不能得到足夠的信息從子程序返回,以完成它的任務。就像一個人步行穿過沙漠。如果他依賴于他的足迹走回頭路,當沙暴來襲抹去了這些痕迹時,他将迷失在沙漠中。這個問題比程序僅僅迷失方向嚴重多了。入侵者用精心編寫的入侵代碼(一種惡意程序)使緩沖區溢出,然後告訴程序依據預設的方法處理緩沖區,并且執行。此時的程序已經完全被入侵者操縱了。

入侵者經常改編現有的應用程序運行不同的程序。例如,一個入侵者能啟動一個新的程序,發送秘密文件(支票本記錄,口令文件,或财産清單)給入侵者的電子郵件。這就好像不僅僅是沙暴吹了腳印,而且後來者也會踩出新的腳印,将我們的迷路者領向不同的地方,他自己一無所知的地方。

應用簡介

緩沖處理

你屋子裡的門和窗戶越少,入侵者進入的方式就越少……

由于緩沖區溢出是一個編程問題,所以隻能通過修複被破壞的程序的代碼而解決問題。如果你沒有源代碼,從上面“堆棧溢出攻擊”的原理可以看出,要防止此類攻擊,我們可以:

1、開放程序時仔細檢查溢出情況,不允許數據溢出緩沖區。由于編程和編程語言的原因,這非常困難,而且不适合大量已經在使用的程序;

2、使用檢查堆棧溢出的編譯器或者在程序中加入某些記号,以便程序運行時确認禁止黑客有意造成的溢出。問題是無法針對已有程序,對新程序來講,需要修改編譯器;

3、經常檢查你的操作系統和應用程序提供商的站點,一旦發現他們提供的補丁程序,就馬上下載并且應用在系統上,這是最好的方法。但是系統管理員總要比攻擊者慢一步,如果這個有問題的軟件是可選的,甚至是臨時的,把它從你的系統中删除。舉另外一個例子,你屋子裡的門和窗戶越少,入侵者進入的方式就越少。

問題提出

内存溢出與數據庫鎖表的問題,可以說是開發人員的噩夢,一般的程序異常,總是可以知道在什麼時候或是在什麼操作步驟上出現了異常,而且根據堆棧信息也很容易定位到程序中是某處出現了問題。内存溢出與鎖表則不然,一般現象是操作一般時間後系統越來越慢,直到死機,但并不能明确是在什麼操作上出現的,發生的時間點也沒有規律,查看日志或查看數據庫也不能定位出問題的代碼。

更嚴重的是内存溢出與數據庫鎖表在系統開發和單元測試階段并不容易被發現,當系統正式上線一般時間後,操作的并發量上來了,數據也積累了一些,系統就容易出現内存溢出或是鎖表的現象,而此時系統又不能随意停機或重啟,為修正BUG帶來很大的困難。

本文以筆者開發和支持的多個項目為例,與大家分享在開發過程中遇到的Java内存溢出和數據庫鎖表的檢測和處理解決過程。

定義及原因

内存溢出是指應用系統中存在無法回收的内存或使用的内存過多,最終使得程序運行要用到的内存大于虛拟機能提供的最大内存。為了解決Java中内存溢出問題,我們首先必須了解Java是如何管理内存的。Java的内存管理就是對象的分配和釋放問題。在Java中,内存的分配是由程序完成的,而内存的釋放是由垃圾收集器(GarbageCollection,GC)完成的,程序員不需要通過調用GC函數來釋放内存,因為不同的JVM實現者可能使用不同的算法管理GC,有的是内存使用到達一定程度時,GC才開始工作,也有定時執行的,有的是中斷式執行GC。但GC隻能回收無用并且不再被其它對象引用的那些對象所占用的空間。Java的内存垃圾回收機制是從程序的主要運行對象開始檢查引用鍊,當遍曆一遍後發現沒有被引用的孤立對象就作為垃圾回收。

引起内存溢出的原因有很多種,常見的有以下幾種:

内存中加載的數據量過于龐大,如一次從數據庫取出過多數據;

集合類中有對對象的引用,使用完後未清空,使得JVM不能回收;

代碼中存在死循環或循環産生過多重複的對象實體;

使用的第三方軟件中的BUG;

啟動參數内存值設定的過小;

解決方法

内存溢出雖然很棘手,但也有相應的解決辦法,可以按照從易到難,一步步的解決。

第一步,就是修改JVM啟動參數,直接增加内存。這一點看上去似乎很簡單,但很容易被忽略。JVM默認可以使用的内存為64M,Tomcat默認可以使用的内存為128MB,對于稍複雜一點的系統就會不夠用。在某項目中,就因為啟動參數使用的默認值,經常報“OutOfMemory”錯誤。因此,-Xms,-Xmx參數一定不要忘記加。

第二步,檢查錯誤日志,查看“OutOfMemory”錯誤前是否有其它異常或錯誤。在一個項目中,使用兩個數據庫連接,其中專用于發送短信的數據庫連接使用DBCP連接池管理,用戶為不将短信發出,有意将數據庫連接用戶名改錯,使得日志中有許多數據庫連接異常的日志,一段時間後,就出現“OutOfMemory”錯誤。經分析,這是由于DBCP連接池BUG引起的,數據庫連接不上後,沒有将連接釋放,最終使得DBCP報“OutOfMemory”錯誤。經過修改正确數據庫連接參數後,就沒有再出現内存溢出的錯誤。

查看日志對于分析内存溢出是非常重要的,通過仔細查看日志,分析内存溢出前做過哪些操作,可以大緻定位有問題的模塊。

第三步,安排有經驗的編程人員對代碼進行走查和分析,找出可能發生内存溢出的位置。重點排查以下幾點:

檢查代碼中是否有死循環或遞歸調用。

檢查是否有大循環重複産生新對象實體。

檢查對數據庫查詢中,是否有一次獲得全部數據的查詢。一般來說,如果一次取十萬條記錄到内存,就可能引起内存溢出。這個問題比較隐蔽,在上線前,數據庫中數據較少,不容易出問題,上線後,數據庫中數據多了,一次查詢就有可能引起内存溢出。因此對于數據庫查詢盡量采用分頁的方式查詢。

檢查List、MAP等集合對象是否有使用完後,未清除的問題。List、MAP等集合對象會始終存有對對象的引用,使得這些對象不能被GC回收。

第四步,使用内存查看工具動态查看内存使用情況。某個項目上線後,每次系統啟動兩天後,就會出現内存溢出的錯誤。這種情況一般是代碼中出現了緩慢的内存洩漏,用上面三個步驟解決不了,這就需要使用内存查看工具了。

内存查看工具有許多,比較有名的有:Optimizeit Profiler、JProbeProfiler、JinSight和Java1.5的Jconsole等。它們的基本工作原理大同小異,都是監測Java程序運行時所有對象的申請、釋放等動作,将内存管理的所有信息進行統計、分析、可視化。開發人員可以根據這些信息判斷程序是否有内存洩漏問題。一般來說,一個正常的系統在其啟動完成後其内存的占用量是基本穩定的,而不應該是無限制的增長的。持續地觀察系統運行時使用的内存的大小,可以看到在内存使用監控窗口中是基本規則的鋸齒形的圖線,如果内存的大小持續地增長,則說明系統存在内存洩漏問題。通過間隔一段時間取一次内存快照,然後對内存快照中對象的使用與引用等信息進行比對與分析,可以找出是哪個類的對象在洩漏。

通過以上四個步驟的分析與處理,基本能處理内存溢出的問題。當然,在這些過程中也需要相當的經驗與敏感度,需要在實際的開發與調試過程中不斷積累。

遊戲問題

有的遊戲在XP SP2系統下會出現内存溢出問題,比如在九陰真經、紅色警戒3、穿越火線 等遊戲時出現死機、電腦自動重啟等現象,解決方法是将系統升級到SP3或更換XP SP3系統

相關詞條

相關搜索

其它詞條