内存泄露、内存溢出以及解决方法
目錄(?)[+]
內(nèi)存泄露是指程序在運(yùn)行過程中動(dòng)態(tài)申請的內(nèi)存空間不再使用后沒有及時(shí)釋放,從而很可能導(dǎo)致應(yīng)用程序內(nèi)存無線增長。更廣義的內(nèi)存泄露包括未對系統(tǒng)的資源的及時(shí)釋放,比如句柄等。
內(nèi)存溢出即用戶在對其數(shù)據(jù)緩沖區(qū)操作時(shí),超過了其緩沖區(qū)的邊界;尤其是對緩沖區(qū)寫操作時(shí),緩沖區(qū)的溢出很可能導(dǎo)致程序的異常。
一.內(nèi)存泄露
“知己知彼,方能百戰(zhàn)不殆”,如果我們能夠比較清楚的了解在編程的時(shí)候哪些情況容易導(dǎo)致內(nèi)存泄露,通過避免這些糟糕的情況,從提高代碼的質(zhì)量本身出發(fā),來抵御潛在導(dǎo)致內(nèi)存泄露的發(fā)生。
1.1先來看看內(nèi)存泄露可能發(fā)生的一些場景:
(1)程序員常常忽略在所有的分支都加上內(nèi)存的回收處理
[cpp]?view plaincopy(2)構(gòu)造函數(shù)中申請空間,析構(gòu)函數(shù)中釋放空間
(3)庫函數(shù)或者系統(tǒng)API會(huì)在內(nèi)部申請空間,然后返回指針給用戶;以strdup為例
[cpp]?view plaincopy? strdup申請了一段空間存儲(chǔ)字符串"hello World",然后返回空間地址,這個(gè)時(shí)候用戶經(jīng)常會(huì)忘記釋放str;
上面只是列出了簡單的三種情況,尤其在一個(gè)復(fù)雜的大型系統(tǒng)中,一段內(nèi)存的使用周期太長或者嵌套太深,還需要程序員自己去把握。
1.2.內(nèi)存泄露的檢測
(1)利用內(nèi)存泄露檢測工具
常用的有 BoundsCheaker、Deleaker、Visual Leak Detector等,工具畢竟熟能生巧,用戶選擇先自己喜歡的一款去用即可。
BoundsChecker沒有找到win7下支持VS2005的破解版,用盜版的傷不起啊。
(2)使用Deleaker(本文采用vs2005)進(jìn)行內(nèi)存泄露檢查
如下圖所示:
A) Deleak安裝后自動(dòng)集成到VS中,在VS“工具”菜單中會(huì)加入一個(gè)“Deleaker”菜單項(xiàng)。
B) Deleaker能夠?qū)DI,USER對象以及句柄進(jìn)行檢測,是否及時(shí)釋放。
C) Deleaker能夠檢測泄露的內(nèi)存發(fā)生地點(diǎn),即展示其函數(shù)棧;雙擊能夠轉(zhuǎn)到相應(yīng)的文件;
PS:Deleaker對中文不支持
如果有內(nèi)存泄露Deleaker會(huì)在程序調(diào)試完彈出對話框如下圖所示:
(3)使用Viual Leak detector
使用Deleak方便靈活,除了其對中文路徑支持問題,但感覺和vs的集成度并不是很高。
Viual Leak detector安裝后,要在VS中設(shè)置相應(yīng)的頭文件和庫路徑,在Debug模式下如果要檢測相應(yīng)源文件的內(nèi)存泄露,則加上"#include <vld.h>"即可;
這樣在檢測內(nèi)存泄露,可以在VS的輸出窗口進(jìn)行輸出,感覺和VS的集成度更高,結(jié)果如下圖所示:
同樣能夠顯示 內(nèi)存泄露處的 調(diào)用棧,并且通過雙擊也可以跳轉(zhuǎn)到文件的內(nèi)存泄露行,個(gè)人還是比較喜歡這種方式的。
(4)在沒有工具的情況下,使用crtdbg.h中的api也是個(gè)很棒的選擇
在MFC中可以看到在程序退出的時(shí)候,輸出框內(nèi)結(jié)尾部分輸出內(nèi)存泄露,并且點(diǎn)擊可以跳轉(zhuǎn)到內(nèi)存泄露的代碼處。
那么在console程序下呢,當(dāng)然我們同樣可以做到(做那些MFC幫我們完成了的細(xì)節(jié));
A) _CrtSetDbgFlag函數(shù)
[cpp]?view plaincopy(函數(shù)詳細(xì)信息參考:http://msdn.microsoft.com/zh-cn/library/5at7yxcs.aspx)
這個(gè)函數(shù)用于控制debug模式下堆管理的分配行為;
在main函數(shù)開始處添加:
[cpp]?view plaincopy則如果出現(xiàn)內(nèi)存泄露Debug結(jié)束后,輸出框?qū)⑤敵?#xff1a;
{150}表示申請的第150塊申請的內(nèi)存空間;B) 顯示內(nèi)存泄露所在的文件以及行
能夠知道有內(nèi)存泄露是不夠的,更需要的信息是哪里內(nèi)存泄露了?
我們可以在每個(gè)源文件的開頭定義寫這樣一條宏定義:
[cpp]?view plaincopyC) 顯示內(nèi)存泄露處的堆棧
[cpp]?view plaincopy此函數(shù)在指定的申請堆區(qū)空間次序處(即lBreakAlloc)設(shè)置斷點(diǎn);
很喜歡這個(gè)函數(shù),這個(gè)函數(shù)結(jié)合"A)"中提到的{150},比如使用方法:
[cpp]?view plaincopy個(gè)人感覺這種方式雖然要手動(dòng)的修改代碼,但其功能卻比前兩個(gè)工具的有效,因?yàn)槟軌蛟?span style="color:rgb(255,0,0)">程序運(yùn)行的時(shí)候查看調(diào)用棧,這就意味著能夠調(diào)試程序。
展示結(jié)果如下圖所示(自動(dòng)在第150次申請堆空間處中斷):
二.內(nèi)存溢出
本篇最想分享的就是內(nèi)存溢出的調(diào)試方法,內(nèi)存溢出能夠?qū)е鲁绦虍惓?#xff0c;而且這種異常使程序員難以下手。2.1 內(nèi)存溢出導(dǎo)致的異常癥狀
(1)內(nèi)存異常經(jīng)常產(chǎn)生的程序報(bào)錯(cuò),如下圖所示:(2)有可能調(diào)試的時(shí)候不錯(cuò),運(yùn)行的時(shí)候出錯(cuò),而且隨機(jī)出現(xiàn),這絕對讓人很頭疼的問題。
(3)慶幸的是,如果編譯后的debug程序,直接運(yùn)行后,如果出錯(cuò),可以選擇調(diào)試程序(如下圖所示);
千萬別以為麻煩就此可以解決了,進(jìn)入調(diào)試狀態(tài)后,發(fā)現(xiàn)出錯(cuò)的地方根本代碼沒有任何問題,可見內(nèi)存溢出是個(gè)多么令人討厭的家伙;
2.2 解決方法
雖然他是那么可惡,但也不要忘了是程序員自己一手創(chuàng)建了出來的。也不要灰心,困難總是有方法去解決的。
(1)等到生病的時(shí)候,再去看病,或許已經(jīng)晚了;最好是提前做好預(yù)防準(zhǔn)備;
? ? ? ? A) 比如在程序中多使用strcpy_s、memcpy_s等具有緩沖區(qū)大小檢查的函數(shù),去取代strcpy、memcpy等;
? ? ? ? B)給工程設(shè)置編譯選項(xiàng)/WX開啟(“將警告視為錯(cuò)誤”),嚴(yán)格要求自己,這樣很可能避免了不少潛在的bug;
? ? ? ? C) ?對自己的代碼做好單元測試
(2)如果出現(xiàn)了這種難以查找的錯(cuò)誤,可以從程序源碼著手,查看一些和內(nèi)存操作相關(guān)的函數(shù),比如strcpy、memcpy等。
本人曾經(jīng)在項(xiàng)目中就遇到用一個(gè)項(xiàng)目組成員在使用,strcpy拷貝一個(gè)字符串到一個(gè)空間不夠的內(nèi)存,從而導(dǎo)致程序異常:
[cpp]?view plaincopy(3)檢查工具
幸運(yùn)的是本人接觸了一個(gè)代碼量較大的工程,不幸的是發(fā)生了內(nèi)存溢出問題,而導(dǎo)致程序異常。而且出現(xiàn)的癥狀,就是調(diào)試不錯(cuò),運(yùn)行出錯(cuò),
而且隨機(jī)出現(xiàn),并且內(nèi)存異常的代碼處,代碼沒有任何問題。這個(gè)問題糾結(jié)了至少一個(gè)月,病極亂投醫(yī),但找了一些工具大多用于檢查內(nèi)存泄露的。
最終確定了兩個(gè)工具:
A)BoudsChecker,除了能夠檢查內(nèi)存泄露,也能檢查內(nèi)存溢出問題;可惜的是沒有找到Win7 下支持VS2005的破解版本
B)AppVerifier,專門用來檢測那些用普通方法檢測不出的意想不到的bug(比如內(nèi)存溢出、錯(cuò)誤句柄使用等)。而且AppVerifier使用非常簡單,
只需要綁定需要測試的的應(yīng)用程序,并且勾選測試項(xiàng)后保存,使用VS2005進(jìn)行調(diào)試即可。AppVier:
PS:文中所稱的內(nèi)存溢出,用英文專業(yè)術(shù)語叫做heap corruption
總結(jié)
以上是生活随笔為你收集整理的内存泄露、内存溢出以及解决方法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安全编程: 防止缓冲区溢出
- 下一篇: windbg调试堆破坏