1. 程式人生 > >記憶體洩露、記憶體溢位以及解決方法

記憶體洩露、記憶體溢位以及解決方法

記憶體洩露是指程式在執行過程中動態申請的記憶體空間不再使用後沒有及時釋放,從而很可能導致應用程式記憶體無線增長。更廣義的記憶體洩露包括未對系統的資源的及時釋放,比如控制代碼等。

記憶體溢位即使用者在對其資料緩衝區操作時,超過了其緩衝區的邊界;尤其是對緩衝區寫操作時,緩衝區的溢位很可能導致程式的異常。

一.記憶體洩露

“知己知彼,方能百戰不殆”,如果我們能夠比較清楚的瞭解在程式設計的時候哪些情況容易導致記憶體洩露,通過避免這些糟糕的情況,從提高程式碼的質量本身出發,來抵禦潛在導致記憶體洩露的發生。

1.1先來看看記憶體洩露可能發生的一些場景:

(1)程式設計師常常忽略在所有的分支都加上記憶體的回收處理

int size = 100;
char *pointer = new char[size];
if (!xxxAPI(pointer, size)
{
    return;
}
delete[]pointer;

(2)建構函式中申請空間,解構函式中釋放空間

(3)庫函式或者系統API會在內部申請空間,然後返回指標給使用者;以strdup為例

char *str;
str = strdup("hello World!");

  strdup申請了一段空間儲存字串"hello World",然後返回空間地址,這個時候使用者經常會忘記釋放str;

上面只是列出了簡單的三種情況,尤其在一個複雜的大型系統中,一段記憶體的使用週期太長或者巢狀太深,還需要程式設計師自己去把握。

1.2.記憶體洩露的檢測

(1)利用記憶體洩露檢測工具

常用的有 BoundsCheaker、Deleaker、Visual Leak Detector等,工具畢竟熟能生巧,使用者選擇先自己喜歡的一款去用即可。

BoundsChecker沒有找到win7下支援VS2005的破解版,用盜版的傷不起啊。

(2)使用Deleaker(本文采用vs2005)進行記憶體洩露檢查

如下圖所示:

A) Deleak安裝後自動整合到VS中,在VS“工具”選單中會加入一個“Deleaker”選單項。

B) Deleaker能夠對GDI,USER物件以及控制代碼進行檢測,是否及時釋放。

C) Deleaker能夠檢測洩露的記憶體發生地點,即展示其函式棧;雙擊能夠轉到相應的檔案;

PS:Deleaker對中文不支援

如果有記憶體洩露Deleaker會在程式除錯完彈出對話方塊如下圖所示:


(3)使用Viual Leak detector

使用Deleak方便靈活,除了其對中文路徑支援問題,但感覺和vs的整合度並不是很高。

Viual Leak detector安裝後,要在VS中設定相應的標頭檔案和庫路徑,在Debug模式下如果要檢測相應原始檔的記憶體洩露,則加上"#include <vld.h>"即可;

這樣在檢測記憶體洩露,可以在VS的輸出視窗進行輸出,感覺和VS的整合度更高,結果如下圖所示:


同樣能夠顯示 記憶體洩露處的 呼叫棧,並且通過雙擊也可以跳轉到檔案的記憶體洩露行,個人還是比較喜歡這種方式的。

(4)在沒有工具的情況下,使用crtdbg.h中的api也是個很棒的選擇

在MFC中可以看到在程式退出的時候,輸出框內結尾部分輸出記憶體洩露,並且點選可以跳轉到記憶體洩露的程式碼處。

那麼在console程式下呢,當然我們同樣可以做到(做那些MFC幫我們完成了的細節);

A) _CrtSetDbgFlag函式

int _CrtSetDbgFlag(
int newFlag
);

這個函式用於控制debug模式下堆管理的分配行為;

在main函式開始處新增:

_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
//_CRTDBG_REPORT_FLAG:表示獲取當前的標示位
//_CRTDBG_LEAK_CHECK_DF:表示檢測記憶體洩露

則如果出現記憶體洩露Debug結束後,輸出框將輸出:

{150}表示申請的第150塊申請的記憶體空間;

B) 顯示記憶體洩露所在的檔案以及行

能夠知道有記憶體洩露是不夠的,更需要的資訊是哪裡記憶體洩露了?

我們可以在每個原始檔的開頭定義寫這樣一條巨集定義:

//根據__FILE___和__LINE__能夠確定檔案和行
#define new   new(_NORMAL_BLOCK, __FILE__, __LINE__)  


C) 顯示記憶體洩露處的堆疊

//lBreakAlloc,在申請的堆區序號為lBreakAlloc處設定一個斷點
long _CrtSetBreakAlloc( long lBreakAlloc );
(函式詳細資訊參考:http://technet.microsoft.com/zh-cn/library/aa246759

此函式在指定的申請堆區空間次序處(即lBreakAlloc)設定斷點;

很喜歡這個函式,這個函式結合"A)"中提到的{150},比如使用方法:

_CrtSetBreakAlloc(150); //則在第150次申請堆空間時候設定斷點
這樣就可以看到函式呼叫棧,從而幫助我們更加精確的定位程式洩露的位置(呼叫棧可是個好玩意)。

個人感覺這種方式雖然要手動的修改程式碼,但其功能卻比前兩個工具的有效,因為能夠在程式執行的時候檢視呼叫棧,這就意味著能夠除錯程式

展示結果如下圖所示(自動在第150次申請堆空間處中斷):


二.記憶體溢位

本篇最想分享的就是記憶體溢位的除錯方法,記憶體溢位能夠導致程式異常,而且這種異常使程式設計師難以下手。

2.1 記憶體溢位導致的異常症狀

(1)記憶體異常經常產生的程式報錯,如下圖所示:

(2)有可能除錯的時候不錯,執行的時候出錯,而且隨機出現,這絕對讓人很頭疼的問題。

(3)慶幸的是,如果編譯後的debug程式,直接執行後,如果出錯,可以選擇除錯程式(如下圖所示);

千萬別以為麻煩就此可以解決了,進入除錯狀態後,發現出錯的地方根本程式碼沒有任何問題,可見記憶體溢位是個多麼令人討厭的傢伙;

2.2 解決方法

雖然他是那麼可惡,但也不要忘了是程式設計師自己一手建立了出來的。也不要灰心,困難總是有方法去解決的。

(1)等到生病的時候,再去看病,或許已經晚了;最好是提前做好預防準備;

        A) 比如在程式中多使用strcpy_s、memcpy_s等具有緩衝區大小檢查的函式,去取代strcpy、memcpy等;

        B)給工程設定編譯選項/WX開啟(“將警告視為錯誤”),嚴格要求自己,這樣很可能避免了不少潛在的bug;

        C)  對自己的程式碼做好單元測試

(2)如果出現了這種難以查詢的錯誤,可以從程式原始碼著手,檢視一些和記憶體操作相關的函式,比如strcpy、memcpy等。

本人曾經在專案中就遇到用一個專案組成員在使用,strcpy拷貝一個字串到一個空間不夠的記憶體,從而導致程式異常:

//拷貝字串,並且返回新的字串地址
char * string_copy(const char *source)
{
    char *p_string;
    int string_len;
    string_len = strlen(source);
    if(source == NULL)
    {
        p_string = (char *)malloc(2*sizeof(char));
        strcpy(p_string, "");
    }
    else
    {	//這裡錯誤 string_len+1
 	p_string = (char *)malloc((string_len)*sizeof(char)); 
	strcpy(p_string, source);
    }
    return p_string;
}
靜態地去檢查程式碼方法比較慢,而且不適用於大工程。

(3)檢查工具

幸運的是本人接觸了一個程式碼量較大的工程,不幸的是發生了記憶體溢位問題,而導致程式異常。而且出現的症狀,就是除錯不錯,執行出錯,

而且隨機出現,並且記憶體異常的程式碼處,程式碼沒有任何問題。這個問題糾結了至少一個月,病極亂投醫,但找了一些工具大多用於檢查記憶體洩露的。

最終確定了兩個工具:

A)BoudsChecker,除了能夠檢查記憶體洩露,也能檢查記憶體溢位問題;可惜的是沒有找到Win7 下支援VS2005的破解版本

B)AppVerifier,專門用來檢測那些用普通方法檢測不出的意想不到的bug(比如記憶體溢位、錯誤控制代碼使用等)。而且AppVerifier使用非常簡單,

只需要繫結需要測試的的應用程式,並且勾選測試項後儲存,使用VS2005進行除錯即可。AppVier:

PS:文中所稱的記憶體溢位,用英文專業術語叫做heap corruption

相關推薦

記憶體洩露記憶體溢位以及解決方法

記憶體洩露是指程式在執行過程中動態申請的記憶體空間不再使用後沒有及時釋放,從而很可能導致應用程式記憶體無線增長。更廣義的記憶體洩露包括未對系統的資源的及時釋放,比如控制代碼等。 記憶體溢位即使用者在對其資料緩衝區操作時,超過了其緩衝區的邊界;尤其是對緩衝區寫操作時,緩衝區的

Android之記憶體洩露記憶體溢位記憶體抖動分析

記憶體 JAVA是在JVM所虛擬出的記憶體環境中執行的,記憶體分為三個區:堆、棧和方法區。 棧(stack):是簡單的資料結構,程式執行時系統自動分配,使用完畢後自動釋放。優點:速度快。 堆(heap):用於存放由new建立的物件和陣列。在堆中分配的記憶體,一方面由jav

解釋:記憶體溢位記憶體洩露記憶體越界緩衝區溢位溢位

記憶體溢位就是你要求分配的記憶體超出了系統能給你的,系統不能滿足 需求,於是產生溢位。 ================================================================ 記憶體洩漏是指你向系統申請分配記憶體進行使用

記憶體洩露記憶體溢位和堆外記憶體,JVM優化引數配置引數

記憶體洩漏 記憶體洩漏是指程式在申請記憶體後,無法釋放已申請的記憶體空間,無用物件(不再使用的物件)持續佔有記憶體或無用物件的記憶體得不到及時釋放,從而造成記憶體空間的浪費。記憶體洩漏最終會導致OOM。 造成記憶體洩漏典型場景: 1. 單例模式的不正確使用單例物件在初始化後將在JVM的整個生命週期中以靜態變數

記憶體溢位記憶體洩漏的區別產生原因以及解決方案【轉】

(轉自:https://www.cnblogs.com/Sharley/p/5285045.html) 記憶體溢位 out of memory,是指程式在申請記憶體時,沒有足夠的記憶體空間供其使用,出現out of memory;比如申請了一個integer,但給它存了long才能存下的數,那就

記憶體溢位記憶體洩漏的區別產生原因以及解決方案

記憶體溢位 out of memory,是指程式在申請記憶體時,沒有足夠的記憶體空間供其使用,出現out of memory;比如申請了一個integer,但給它存了long才能存下的數,那就是記憶體溢位。 記憶體洩露 memory leak,是指程式在申請記憶體後,無法釋

溢位記憶體洩漏的區別產生原因以及解決方案

  記憶體溢位的原因以及解決方法 引起記憶體溢位的原因有很多種,小編列舉一下常見的有以下幾種: 1.記憶體中載入的資料量過於龐大,如一次從資料庫取出過多資料; 2.集合類中有對物件的引用,使用完後未清空,使得JVM不能回收; 3.程式碼中存在死迴圈或迴圈產生過多重複的物件實體; 4.使用的第三方軟體中

Mysql遍歷大表(Mysql大量資料讀取記憶體溢位解決方法

mysql jdbc預設把select的所有結果全部取回,放到記憶體中,如果是要遍歷很大的表,則可能把記憶體撐爆。 一種辦法是:用limit,offset,但這樣你會發現取資料的越來越慢,原因是設定了offset,mysql需要將讀取位置移動到offset的位置,隨著offset增大,取資料也越來越慢

記憶體洩露記憶體溢位的區別 (概念區別 產生原因區別 及解決辦法) 個人整理

記憶體洩露和記憶體溢位的區別 概念區別 記憶體溢位 : out of memory 指程式在申請記憶體時,沒有足夠的記憶體空間供其使用,出現out fo memory 比如申請一個integer 但給它存了long才能存下的數那就是記憶體溢位 記憶體洩露 : memory leak 指程

Java虛擬機器7:記憶體溢位記憶體洩露並行和併發Minor GC和Full GCClient模式和Server模式的區別

記憶體溢位和記憶體洩露的區別 1、記憶體溢位 記憶體溢位指的是程式在申請記憶體的時候,沒有足夠大的空間可以分配了。 2、記憶體洩露 記憶體洩露指的是程式在申請記憶體之後,沒有辦法釋放掉已經申請到記憶體,它始終佔用著記憶體,即被分配的物件可達但無用。記憶體洩露一般都是因

【轉】從JVM模型談十種記憶體溢位解決方法

原帖地址:https://www.jianshu.com/p/666f0ddb475c 導言: 對於java程式設計師來說,在虛擬機器自動記憶體管理機制的幫助下,不需要自己實現釋放記憶體,不容易出現記憶體洩漏和記憶體溢位的問題,由虛擬機器管理記憶體這一切看起來非常美好,但是一旦出現記憶體溢位或者

從JVM模型談十種記憶體溢位解決方法

導言: 對於java程式設計師來說,在虛擬機器自動記憶體管理機制的幫助下,不需要自己實現釋放記憶體,不容易出現記憶體洩漏和記憶體溢位的問題,由虛擬機器管理記憶體這一切看起來非常美好,但是一旦出現記憶體溢位或者記憶體洩漏的問題,對於不熟悉jvm虛擬機器是怎麼使用記憶體的話,那麼排查錯誤將會是一項非

heap out of memory nodejs 記憶體溢位解決方法

今天在nodejs請求sql資料庫後,想將資料儲存到excle裡,測試時候資料量少,很完美的將資料導了出來,但當資料量太大時,資料匯出後達到200多M, 報錯:heap out of memory 解決

MyEclipse記憶體溢位問題解決方法

往MyEclipse  Window/Preferences/MyEclipse……/Servers/Tomcat/ Tomcat 6.x/JDK Optional Java VM arguments: 輸入:-Xms256m -Xmx512m -XX:MaxNewSize=128m -XX:MaxPerm

MyEclipse 2015 執行tomcat 記憶體溢位解決方法

記憶體溢位錯誤: 2016-3-16 11:19:55 org.apache.catalina.core.StandardWrapperValve invoke 嚴重: Servlet.service() for servlet default threw except

java專案記憶體溢位問題解決方法

 }      3 寫程式碼的時候處理記憶體溢位             try{                //do   sth      ....             }catch   (outofmemoryerror   e){//可以用一個共通函式來執行.              syst

安卓載入圖片過大而導致OOM記憶體溢位解決方法(巨坑....)

如果圖片太大會造成OOM記憶體溢位的錯誤,需要用Bitmap的壓縮機制。 如果跳轉的頁面含有圖片可能會導致跳轉失敗。 比如說我這裡是一旦觸發了某個按鍵,就修改該xml的圖片和文字說明 則setImageResource應該改成這樣imageview.setImageBitm

Android ADT 23.0.0 64位的下載地址及官方下載+記憶體溢位錯誤解決方法

64位免費下載:點選下載 安裝好後會出現這樣: An out of memory error has occurred.Consult the "Running Eclipse" section

Netty4.1.6版本 記憶體洩漏問題以及解決方法

    最近在專案中遇到一個問題,是關於Netty4.1.6版本,在日誌中列印以下錯誤訊息, i.n.channel.DefaultChannelPipeline - An exceptionCaught() event was fired, and it reached

android很多圖片做成幀動畫造成記憶體溢位解決方法

package com.familydoctor.widget; import android.os.Handler; import android.util.Log; import android.widget.ImageView; import com.familydo