天天看點

記憶體洩露

 記憶體洩漏也稱作“存儲滲漏”,用動态存儲配置設定函數動态開辟的空間,在使用完畢後未釋放,結果導緻一直占據該記憶體單元。直到程式結束。(其實說白了就是該記憶體空間使用完畢之後未回收)即所謂記憶體洩漏。

記憶體洩漏形象的比喻是“作業系統可提供給所有程序的存儲空間正在被某個程序榨幹”,最終結果是程式運作時間越長,占用存儲空間越來越多,最終用盡全部存儲空間,整個系統崩潰。是以“記憶體洩漏”是從作業系統的角度來看的。這裡的存儲空間并不是指實體記憶體,而是指虛拟記憶體大小,這個虛拟記憶體大小取決于磁盤交換區設定的大小。由程式申請的一塊記憶體,如果沒有任何一個指針指向它,那麼這塊記憶體就洩漏了。

分類編輯

以發生的方式來分類,記憶體洩漏可以分為4類:

常發性

發生記憶體洩漏的代碼會被多次執行到,每次被執行的時候都會導緻一塊記憶體洩漏。

偶發性

發生記憶體洩漏的代碼隻有在某些特定環境或操作過程下才會發生。常發性和偶發性是相對的。對于特定的環境,偶發性的也許就變成了常發性的。是以測試環境和測試方法對檢測記憶體洩漏至關重要。

一次性

發生記憶體洩漏的代碼隻會被執行一次,或者由于算法上的缺陷,導緻總會有一塊且僅一塊記憶體發生洩漏。比如,在類的構造函數中配置設定記憶體,在析構函數中卻沒有釋放該記憶體,是以記憶體洩漏隻會發生一次。

隐式

程式在運作過程中不停的配置設定記憶體,但是直到結束的時候才釋放記憶體。嚴格的說這裡并沒有發生記憶體洩漏,因為最終程式釋放了所有申請的記憶體。但是對于一個伺服器程式,需要運作幾天、幾周甚至幾個月,不及時釋放記憶體也可能導緻最終耗盡系統的所有記憶體。是以,我們稱這類記憶體洩漏為隐式記憶體洩漏。

危害編輯

從使用者使用程式的角度來看,記憶體洩漏本身不會産生什麼危害,作為一般的使用者,根本感覺不到記憶體洩漏的存在。真正有危害的是記憶體洩漏的堆積,這會最終消耗盡系統所有的記憶體。從這個角度來說,一次性記憶體洩漏并沒有什麼危害,因為它不會堆積,而隐式記憶體洩漏危害性則非常大,因為較之于常發性和偶發性記憶體洩漏它更難被檢測到。

表現編輯

記憶體洩漏或者是說,資源耗盡後,系統會表現出什麼現象啊?

cpu資源耗盡:估計是機器沒有反應了,鍵盤,滑鼠,以及網絡等等。這個在windows上經常看見,特别是中了毒。

程序id耗盡:沒法建立新的程序了,序列槽或者telnet都沒法建立了。

硬碟耗盡: 機器要死了,交換記憶體沒法用,日志也沒法用了,死是很正常的。

記憶體洩漏或者記憶體耗盡:新的連接配接無法建立,free的記憶體比較少。發生記憶體洩漏的程式很多,但是要想産生一定的後果,就需要這個程序是無限循環的,是個服務程序。當然,核心也是無限循環的,是以,如果核心發生了記憶體洩漏,情況就更加不妙。記憶體洩漏是一種很難定位和跟蹤的錯誤,目前還沒看到有什麼好用的工具(當然,使用者空間有一些工具,有靜态分析的,也會動态分析的,但是找核心的記憶體洩漏,沒有好的開源工具)。

記憶體洩漏和對象的引用計數有很大的關系,再加上c/c++都沒有自動的垃圾回收機制,如果沒有手動釋放記憶體,問題就會出現。如果要避免這個問題,還是要從代碼上入手,良好的編碼習慣和規範,是避免錯誤的不二法門。

一般我們常說的記憶體洩漏是指堆記憶體的洩漏。

堆記憶體是指程式從堆中配置設定的,大小任意的(記憶體塊的大小可以在程式運作期決定),使用完後必須顯式釋放的記憶體。

應用程式一般使用malloc,realloc,new等函數從堆中配置設定到一塊記憶體,使用完後,程式必須負責相應的調用free或delete釋放該記憶體塊,否則,這塊記憶體就不能被再次使用,我們就說這塊記憶體洩漏了。

使用了記憶體配置設定的函數,要記得要使用其想用的函數釋放掉,一旦使用完畢。      
Heap memory:      
malloc\realloc ------  free      
new \new[] ----------  delete \delete[]      
GlobalAlloc------------GlobalFree      
要特别注意數組對象的記憶體洩漏      
     MyPointEX *pointArray =new MyPointEX [100];      
      其删除形式為:      

     delete []pointArray 

由記憶體洩露引出記憶體溢出話題:      
所謂記憶體溢出就是你要求配置設定的記憶體超出了系統能給你的,系統不能滿足需求,于是會産生記憶體溢出的問題。      
常見的溢出主要有:      
記憶體配置設定未成功,卻使用了它。
常用解決辦法是,在使用記憶體之前檢查指針是否為NULL。如果指針p 是函數的參數,那麼在函數的入口處用assert(p!=NULL)進行檢查。如果是用malloc 或new 來申請記憶體,應該用if(p==NULL)或if(p!=NULL)進行防錯處理。      
記憶體配置設定雖然成功,但是尚未初始化就引用它。
記憶體配置設定成功并且已經初始化,但操作越過了記憶體的邊界。
例如在使用數組時經常發生下标“多1”或者“少1”的操作。特别是在for 循環語句中,循環次數很容易搞錯,導緻數組操作越界。      
使用free 或delete 釋放了記憶體後,沒有将指針設定為NULL。導緻産生“野指針”。      

繼續閱讀