目前分類:未分類文章 (353)

瀏覽方式: 標題列表 簡短摘要
首先我們需要知道程序有沒有內存泄露,然後定位到底是哪行代碼出現內存泄露了,這樣才能將其修復。

天才R 發表在 痞客邦 留言(2) 人氣()

作者:freepublic

摘要
本文分析了Windows環境使用MFC調試內存洩露的技術,介紹了在Windows環境下用VC++查找,定位和消除內存洩露的方法技巧。

關鍵詞:VC++;CRT 調試堆函數;試探法。

編譯環境
VC++6.0
技術原理
檢測內存洩漏的主要工具是調試器和 CRT 調試堆函數。若要啟用調試堆函數,請在程序中包括以下語句:

#define CRTDBG_MAP_ALLOC
#include 
#include 

注意 #include 語句必須採用上文所示順序。如果更改了順序,所使用的函數可能無法正確工作。

通過包括 crtdbg.h,將 malloc 和 free 函數映射到其“Debug”版本_malloc_dbg 和_free_dbg,這些函數將跟蹤內存分配和釋放。此映射只在調試版本(在其中定義了 _DEBUG)中發生。發布版本使用普通的 malloc 和 free 函數。

#define 語句將 CRT 堆函數的基版本映射到對應的“Debug”版本。並非絕對需要該語句,但如果沒有該語句,內存洩漏轉儲包含的有用信息將較少。

在添加了上面所示語句之後,可以通過在程序中包括以下語句來轉儲內存洩漏信息:

_CrtDumpMemoryLeaks();

當在調試器下運行程序時,_CrtDumpMemoryLeaks 將在“輸出”窗口中顯示內存洩漏信息。內存洩漏信息如下所示:

Detected memory leaks!

Dumping objects ->

C:PROGRAM FILESVISUAL STUDIOMyProjectsleaktestleaktest.cpp(20) : {18} normal block at 0x00780E80, 64 bytes long.

Data: <        > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete. 

如果不使用 #define _CRTDBG_MAP_ALLOC 語句,內存洩漏轉儲如下所示:

Detected memory leaks! 
Dumping objects -> 
{18} normal block at 0x00780E80, 64 bytes long. 
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete. 

未定義 _CRTDBG_MAP_ALLOC 時,所顯示的會是:

內存分配編號(在大括號內)。
塊類型(普通、客戶端或 CRT)。
十六進制形式的內存位置。
以字節為單位的塊大小。
前 16 字節的內容(亦為十六進制)。
定義了 _CRTDBG_MAP_ALLOC 時,還會顯示在其中分配洩漏的內存的文件。文件名後括號中的數字(本示例中為 20)是該文件內的行號。

轉到源文件中分配內存的行

在"輸出"窗口中雙擊包含文件名和行號的行。
-或-

在"輸出"窗口中選擇包含文件名和行號的行,然後按 F4 鍵。

_CrtSetDbgFlag 

如果程序總在同一位置退出,則調用 _CrtDumpMemoryLeaks 足夠方便,但如果程序可以從多個位置退出該怎麼辦呢?不要在每個可能的出口放置一個對 _CrtDumpMemoryLeaks 的調用,可以在程序開始包括以下調用:

_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); 

該語句在程序退出時自動調用 _CrtDumpMemoryLeaks。必須同時設置 _CRTDBG_ALLOC_MEM_DF 和 _CRTDBG_LEAK_CHECK_DF 兩個位域,如上所示。

說明
在VC++6.0的環境下,不再需要額外的添加

#define CRTDBG_MAP_ALLOC 
#include  
#include  

只需要按F5,在調試狀態下運行,程序退出後在"輸出窗口"可以看到有無內存洩露。如果出現

Detected memory leaks! 
Dumping objects -> 

就有內存洩露。

確定內存洩露的地方
根據內存洩露的報告,有兩種消除的方法:

第一種比較簡單,就是已經把內存洩露映射到源文件的,可以直接在"輸出"窗口中雙擊包含文件名和行號的行。例如

Detected memory leaks! 
Dumping objects -> 
C:PROGRAM FILESVISUAL STUDIOMyProjectsleaktestleaktest.cpp(20) : {18} normal block at 0x00780E80, 64 bytes long. 
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete.
C:PROGRAM FILESVISUAL STUDIOMyProjectsleaktestleaktest.cpp(20)

就是源文件名稱和行號。

第二種比較麻煩,就是不能映射到源文件的,只有內存分配塊號。

Detected memory leaks! 
Dumping objects -> 
{18} normal block at 0x00780E80, 64 bytes long. 
Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete. 

  這種情況我採用一種"試探法"。由于內存分配的塊號不是固定不變的,而是每次運行都是變化的,所以跟蹤起來很麻煩。但是我發現雖然內存分配的塊號是變化的,但是變化的塊號卻總是那幾個,也就是說多運行幾次,內存分配的塊號很可能會重復。因此這就是"試探法"的基礎。

    先在調試狀態下運行幾次程序,觀察內存分配的塊號是哪幾個值;
    選擇出現次數最多的塊號來設斷點,在代碼中設置內存分配斷點:
    添加如下一行(對于第 18 個內存分配):

    _crtBreakAlloc = 18; 

    或者,可以使用具有同樣效果的 _CrtSetBreakAlloc 函數:

    _CrtSetBreakAlloc(18); 

    在調試狀態下運行序,在斷點停下時,打開"調用堆棧"窗口,找到對應的源代碼處;

    退出程序,觀察"輸出窗口"的內存洩露報告,看實際內存分配的塊號是不是和預設值相同,如果相同,就找到了;如果不同,就重復步驟3,直到相同。
    最後就是根據具體情況,在適當的位置釋放所分配的內存。

(全文完)

天才R 發表在 痞客邦 留言(0) 人氣()

Net資源洩露(內存洩露,GDI洩露,handle 洩露等)的終極解決方案 

摘要

  本文主要討論了,什麼是.Net內存洩露?如何確定是發生了內存洩露?如何預防內存洩露的發生?

正文
1.dot Net內存洩露簡介

   可能很多.Net的用戶(甚至包括一些dot Net開發者)對Net的內存洩露不是很了解,甚至會說.Net不存在內存洩露,因為“不是有GC機制嗎?”----恩,是有這麼回事,它可以讓你在通常應用中不用考慮令人頭疼的資源釋放問題,但很遺憾的是這個機制不保證你開發的程序就不存在內存洩露。甚至可以說,dot Net中內存洩露是很常見的。這是因為:  一方面,GC機制本身的缺陷造成的;另一方面,Net中託管資源和非託管資源的處理是有差異的,託管資源的處理是由GC自動執行的(執行時機是不可預知的),而非託管資源 (佔少部分,比如文件操作,網絡連接等)必須顯式地釋放,否則就可能造成洩露。綜合起來說的話,由于託管資源在Net中佔大多數,通常不做顯式的資源釋放是可以的,不會造成明顯的資源洩露,而非託管資源則不然,是發生問題的主戰場,是最需要注意的地方。 另外,很多情況下,衰老測試主要關注的是有沒有內存洩露的發生,而對其他洩露的重視次之。這是因為,內存跟其他資源是正相關的,也就是說沒有內存洩露的發生,其他洩露的發生概率也較小,其根本原因在于幾乎所有的資源最後都會在內存上有所反應。
2..Net內存洩露的檢測
有沒有內存洩露的發生?判斷依據是那些?
  如果程序報“Out of memory”之類的錯誤,事實上也佔據了很大部分的內存,應該說是典型的內存洩露,這種情況屬于徹底的Bug,解決之道就是找到問題點,改正。但我的經驗中,這種三下兩下的就明顯的洩露的情況較少,除非有人在很困的情況下編碼,否則大多是隱性或漸進式地洩露,這種需經過較長時間的衰老測試才能發現,或者在特定條件下才出現,對這種情況要確定問題比較費勁,有一些工具(詳見1.3)可以利用,但我總感覺效果一般,也可能是我不會使用吧,我想大型程序估計得無可奈何的用這個,詳細的參見相關手冊。
  需要強調的是,判斷一個程序是不是出現了"memory leak",關鍵不是看它佔用的內存有多大,而是放在一個足夠長的時期(程序進入穩定運行狀態後)內,看內存是不是還是一直往上漲,因此,剛開始的漲動或者前期的漲動不能做為洩露的充分證據。
  以上是些比較感性的說法,實際操作中是通過一些性能計數器來測定的。大多數時候,主要關注 Process 裡的以下幾個指標就能得出結論,如果這些量整體來看是持續上升的,基本可以判斷是有洩露情況存在的。
  A.Handle Count
  B.Thread Count
  C.Private Bytes
  D.Virtual Bytes
  E.Working Set
  F.另外.NET CLR Memory下的Bytes in all heeps也是我比較關注的。
  通過觀察,如果發現這些參數是在一個區間內震蕩的,應該是沒有大的問題,但如果是一個持續上漲的狀態,那就得注意,很可能存在內存洩露。
3.內存洩露診斷工具
    3.1perfmon.msc 的使用

       如何測定那些性能計數器呢,大多使用windows自帶的perfmon.msc。

      在Run中輸入perfmon.msc,運行,其他的自己摸索,不難。
   3.2 其他重要的性能計數器

      其他重要的計數器
   3.3其他檢測工具

  我用過的工具中CLRProfiler 和dotTrace 還行,windeg也還行。不過坦白的說,準確定位比較費勁,最好還是按常規的該Dispose的加Dispose,也可以加 GC.Collect()。
4.如何制造出健壯的程序
  4.1 Dispose()的使用

     如果使用的對象提供Dispose()方法,那麼當你使用完畢或在必要的地方(比如Exception)調用該方法,特別是對非託管對象,一定要加以調 用,以達到防止洩露的目的。另外很多時候程序提供對Dispose()的擴展,比如Form,在這個擴展的Dispose方法中你可以把大對象的引用什麼 的在退出前釋放。

   對于DB連接,COM組件(比如OLE組件)等必須調用其提供的Dispose方法,沒有的話最好自己寫一個。
  4.2 using的使用

   using除了引用Dll的功用外,還可以限制對象的適用範圍,當超出這個界限後對象自動釋放,比如
  4.3 事件的卸載

   這個不是必須的,推薦這樣做。之前注冊了的事件,關閉畫面時應該手動注銷,有利于GC回收資源。
  4.4 API的調用

       一般的使用API了就意味著使用了非託管資源,需要根據情況手動釋放所佔資源,特別是在處理大對象時。
  4.5繼承 IDisposable實現自己內存釋放接口

    Net 如何繼承IDisposable接口,實現自己的Dispose()函數
  4.6弱引用(WeakReference )

     通常情況下,一個實例如果被其他實例引用了,那麼他就不會被GC回收,而弱引用的意思是,如果一個實例沒有被其他實例引用(真實引用),而僅僅是被弱引 用,那麼他就會被GC回收。
  4.7析構函數(Finalize())

     使用了非託管資源的時候,可以自定義析構函數使得對象結束時釋放所佔資源;       

     對僅使用託管資源的對象,應盡可能使用它自身的Dispose方法,一般不推薦自定義析構函數。
5.其他資源洩露

   GDI leak,handle leak。
6. 幾個特例

1.對于使用了Bitmap對象的部分需要調用DestroyIcon來刪除對象

 [DllImport("user32", EntryPoint = "DestroyIcon")]
 private static extern void DestroyIcon(IntPtr handle);


 using (System.IO.MemoryStream mstream = new System.IO.MemoryStream())
 {
     bmp.Save(mstream, System.Drawing.Imaging.ImageFormat.Png);
     intP = new Bitmap(mstream).GetHicon(); this.notifyIcon.Icon = Icon.FromHandle(intP);
     DestroyIcon(intP);
 }  

2. 多線程中的GC         

             //Force garbage collection.
            GC.Collect();
            //Wait for all finalizers to complete
            GC.WaitForPendingFinalizers();
7.參考文獻

  1.發 現並防止 託管代碼中出現內存洩漏 ;

  2..NET Memory Leak reader email: Are you really “leaking” .net memory

  3.How to detect and avoid memory and resources leaks in .NET applications

 4.其他參考資料
8.擴展閱讀

1.實 用.net內存洩露(memory leak)解決方案

2.Garbage Collection: Automatic Memory Management in the Microsoft .NET Framework By Jeffrey Richter  http://msdn.microsoft.com/en-us/magazine/bb985010.aspx

3.OutOfMemoryException and Pinning

https://blogs.msdn.com/yunjin/archive/2004/01/27/63642.aspx

4.http://blogs.msdn.com/b/tess/

5.http://msdn.microsoft.com/zh-cn/library/0xy59wtx%28v=VS.80%29.aspx

後記

   其實本文更像是一篇如何開發出更健壯dot Net程序的指南。

天才R 發表在 痞客邦 留言(0) 人氣()

Snapdragon 新成員有哪些特點

天才R 發表在 痞客邦 留言(0) 人氣()

Q1: What distribution are you currently using to port AMSS into your target? For example, Windows, Ubutun, or others?
Re:

天才R 發表在 痞客邦 留言(0) 人氣()

[OS] Qualcomm7
在Qualcomm7系列的架構中,一顆IC內包了二顆ARM處理器,一顆是用ARM9的核心,專門負責處理通訊協定,RF及所有的IO開關控制,而另外一顆則是用ARM11的核心,用來處理多媒體和我們所熟知的作業系統如Linux或Windows等,而ARM9端則是有自己的作業系統,二者之間是透過shared memory的硬體來做溝通,軟體則是使用RPC(Remote Procedure Call)的機制。跑在ARM9上的軟體我們稱之為AMSS(Advanced Mobile Subscriber Software),而跑在ARM11上的就是Linux或Windows了。

天才R 發表在 痞客邦 留言(0) 人氣()



天才R 發表在 痞客邦 留言(0) 人氣()

本文整理自侯捷老師的著作「深入淺出 MFC」。

天才R 發表在 痞客邦 留言(0) 人氣()


天才R 發表在 痞客邦 留言(1) 人氣()

1/ windows的flat 模式: 這題目要講起來可以很深,包含許多的組合語言的東西。但在此我只想說一個結果,就是因為windows是使用這樣的一個記憶體管理位址模式,所以使得每個 process都可以有私有的4G的「定址」能力,但因為windows當初要相容MIPS R4000 architecture,所以高位址的2G部份放的是kernel code,而我們的ap只能用低部份的2G,所以正確的來講,我們的程式能夠使用2G的定址能力。

天才R 發表在 痞客邦 留言(0) 人氣()

C++裡有兩種#include. 一種是新的#include, 尾端並無.h. 這種#include檔裡所有的函式和變數都被分類在std這個分類裡. 所以要用這種#include檔裡所有的函式和變數你要考慮到namespace.

天才R 發表在 痞客邦 留言(0) 人氣()


a.人力資源(HR),這個跟學校裡面學的差不多~

天才R 發表在 痞客邦 留言(3) 人氣()

m_pMainWnd = new CMainWindow;
//m_pMainWnd 是CMyApp的一個成員變量,這句新建了一個CMainWindow類對象,並將其地址賦給m_pMainWnd,以後用這個指針來操作此對象

m_pMainWnd->ShowWindow(m_nCmdShow); 
// 這句決定了CMainWindow對象(派生自CFrameWnd類,這是一個框架類,窗口可能帶菜單、工具欄等等)是否顯示窗口,參數為m_nCmdShow。一般如果你需要顯示窗口就用SW_SHOW這個參數,隱藏窗口就用SW_HIDE。

m_pMainWnd->UpdateWindow(); 
// 刷新窗口,讓其立即重繪

天才R 發表在 痞客邦 留言(0) 人氣()

Last Update: 03/11/2004

主要參考資料:http://www.codeproject.com/debug/mapfile.asp

天才R 發表在 痞客邦 留言(0) 人氣()

2004/5/3 上午 12:43:55

天才R 發表在 痞客邦 留言(0) 人氣()

計算機上都存在一個程序能夠產生的地址集合,我們稱之為地址範圍。這個範圍的大小由CPU的位數決定,例如一個32位的CPU,它的地址範圍是0~0xFFFFFFFF (4G),而對于一個64位的CPU,它的地址範圍為0~0xFFFFFFFFFFFFFFFF (64T)

天才R 發表在 痞客邦 留言(0) 人氣()

fork()和vfork()這兩個系統功能都可以複製出和呼叫者﹙parent﹚完全相同的processchild﹚,但呼叫vfork()後的parent process會被暫停,直到被複製出來的child process執行了exec()exit();而呼叫fork()後的parent process會和新產生的child process平行﹙concurrent﹚執行。

接下來我們必須約略解釋一下fork()Linux中的實現方式,旨在讓讀者知道為什麼這個系統功能沒法直接移植到沒有MMUCPU上;首先我們必須先介紹一下”copy-on-write”這個觀念:

天才R 發表在 痞客邦 留言(0) 人氣()

 


天才R 發表在 痞客邦 留言(0) 人氣()

天才R 發表在 痞客邦 留言(0) 人氣()

前置處理器 #define #undef
#define 大家應該都知道它是用來定義的,而

天才R 發表在 痞客邦 留言(0) 人氣()