MFC內存泄露



?

1、泄露代碼示例

void X::SetApplicationBtn()
{CMFCRibbonApplicationButton* pBtn = GetApplicationButton();// 獲取 Ribbon Bar 指針// 創建自定義按鈕CCustomRibbonAppButton* pCustomButton = new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26);// 替換默認的 Application Buttonthis->SetApplicationButton(pCustomButton, CSize(45, 45));
}

2、CRT?

3、使用CRT調試功能來檢測內存泄漏

筆記小結

1.1 使用方式

// 使用調試堆函數:#define _CRTDBG_MAP_ALLOC
#include<stdlib.h>
#include<crtdbg.h>// 檢測內存泄漏的地方添加下面這條語句來輸出內存泄漏信息
_CrtDumpMemoryLeaks();

1.2 相關介紹

// 分配泄漏內存的文件名,以及文件名后括號中的數字表示發生泄漏的代碼行號
#define _CRTDBG_MAP_ALLOC

1.2-1?內存泄露提示(和上述內存泄露代碼無關)

Detected memory leaks!
Dumping objects ->
D:\a\_work\1\s\src\vctools\crt\github\stl\src\locale0.cpp(34) : {91690} crt block at 0x0000020FDD50EF20, subtype 0, 16 bytes long.Data: <          P     > 00 00 00 00 00 00 00 00 90 F2 50 DD 0F 02 00 00

1.3 內存塊類型

normal(普通)、client(客戶端)、?CRT(運行時)

normal block(普通塊):這是由你的程序分配的內存。
client block(客戶塊):這是一種特殊類型的內存塊,專門用于 MFC 程序中需要析構函數的對象。MFC new 操作符視具體情況既可以為所創建的對象建立普通塊,也可以為之建立客戶塊。
CRT block(CRT 塊):是由 C RunTime Library 供自己使用而分配的內存塊。由 CRT 庫自己來管理這些內存的分配與釋放,我們一般不會在內存泄漏報告中發現 CRT 內存泄漏,除非程序發生了嚴重的錯誤(例如 CRT 庫崩潰)。

1.4? 輸出泄露

// 無論程序在什么地方退出都會自動調用 _CrtDumpMemoryLeak
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

1.5 內存泄露信息?輸出定向到別的地方

//
_CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_DEBUG );

4、設置斷點

內存分配序號:件名和行號之后的花括弧里的那個數字。

????????CRT 庫對程序運行期間分配的所有內存塊進行計數,包括由 CRT 庫自己分配的內存和其它庫(如 MFC)分配的內存。因此,分配序號為 N 的對象即為程序中分配的第 N 個對象,但不一定是代碼分配的第 N 個對象。

????????方法是在程序起始附近設置一個位置斷點。當程序在該點中斷時,可以從 QuickWatch(快速監視)對話框或 Watch(監視)窗口設置一個內存分配斷點。

// Watch 窗口中,在 Name 欄鍵入下面的表達式:_crtBreakAlloc

?Watch 窗口中,在 Name 欄鍵入下面的表達式:

 如果要使用 CRT 庫的多線程 DLL 版本(/MD 選項),那么必須包含上下文操作符,像這樣:
?

{,,msvcrtd.dll}_crtBreakAlloc


  現在按下回車鍵,調試器將計算該值并把結果放入 Value 欄。如果沒有在內存分配點設置任何斷點,該值將為 –1。

5、MFC多文檔使用示例

直接使用_CrtSetDbgFlag | Microsoft Learn

BOOL X::InitInstance()
{// 啟用自動內存泄漏檢測(調試模式)
#ifdef _DEBUG_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif
……
}

特別解釋:

_CRTDBG_LEAK_CHECK_DFOFF打開:在程序退出時通過對?_CrtDumpMemoryLeaks?的調用執行自動泄露檢查,如果應用程序無法釋放它分配的所有內存,則生成錯誤報告。 關閉:不要在程序退出時自動執行泄露檢查。

還可以與任何堆頻率檢查宏組合。

盡量避免不必要得使用:_CrtDumpMemoryLeaks?

6、deepseek的

關于 `_CrtDumpMemoryLeaks` 在 MFC 中的使用位置和避免誤報的問題,你的理解非常正確。`ExitInstance` 和析構函數確實是常見的誤報點,核心原因就在于 檢測時機過早。下面詳細解釋原因并提供解決方案:1. 為什么在 `ExitInstance` 或析構函數中使用 `_CrtDumpMemoryLeaks` 會產生誤報?MFC 框架/靜態對象清理滯后: `ExitInstance` 是 `CWinApp` 的成員函數,它在應用程序的主消息循環結束后、主窗口已銷毀時調用。析構函數(尤其是 `CWinApp` 的析構函數)也發生在這個階段。關鍵點在于:MFC 內部對象: MFC 框架自身可能還有一些內部對象(如文檔模板、線程局部存儲、內部緩存等)需要在 `ExitInstance` 之后或 `CWinApp` 析構之后才被完全清理。全局/靜態對象: 你的代碼或第三方庫中定義的全局對象 (`CMyGlobalObject globalObj;`) 或靜態對象 (`static CMyStaticObject staticObj;`) 的析構函數,它們的執行順序是由編譯器決定的,并且可能發生在 `CWinApp` 析構之后。
檢測時機過早: 在 `ExitInstance` 或 `CWinApp` 析構函數中調用 `_CrtDumpMemoryLeaks` 時,上述那些滯后清理的 MFC 內部對象和全局/靜態對象還沒有被銷毀。這些對象所占用的內存,在 `_CrtDumpMemoryLeaks` 看來就是“尚未釋放”的內存,從而被錯誤地報告為內存泄漏。總結: `ExitInstance` 和析構函數發生在應用程序生命周期的“中間”階段,此時并非所有內存使用者(尤其是全局/靜態對象)都已結束生命并釋放內存。在此處檢測,必然會將那些稍后才釋放的對象誤報為泄漏。2. 如何正確使用 `_CrtDumpMemoryLeaks` 避免誤報?解決方案的核心在于:確保檢測發生在所有用戶代碼(包括全局/靜態對象析構)和 MFC 框架清理工作都已完成之后。 以下是幾種推薦且有效的方法:方法 1: 利用 `_CrtSetDbgFlag` 設置自動報告 (最推薦)原理: 在 `InitInstance` 的最開始設置調試標志,告訴 C 運行時庫在程序完全退出時(即所有全局/靜態對象析構完成后)自動調用 `_CrtDumpMemoryLeaks`。
位置: `CMyApp::InitInstance()` 的開頭。
代碼:```cppBOOL CMyApp::InitInstance(){// 在 InitInstance 最開始設置內存泄漏檢測標志 (僅 Debug 模式)#ifdef _DEBUG_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);#endif// ... 其他初始化代碼 (注冊文檔模板、創建主窗口等) ...return TRUE;}```優點:時機最準確: 檢測發生在程序生命周期的最后(所有全局/靜態對象析構后),最大程度減少誤報。無需手動調用: 自動執行,方便可靠。標準做法: 這是 VC++ 調試內存泄漏的官方推薦方式。
缺點: 輸出在調試器輸出窗口(如 VS 的 Output 窗口),可能不如寫入文件直觀(但通常足夠)。方法 2: 在 `main`/`WinMain` 函數返回前調用 (適用于明確入口點)原理: 如果你的應用程序入口點是標準的 `WinMain` 或 `main` (MFC 通常隱藏了 `WinMain`),可以在入口函數的 `return` 語句之前調用 `_CrtDumpMemoryLeaks`。
位置: `WinMain` 或 `main` 函數結束前 (`return` 之前)。
代碼 (示例 `WinMain`, 通常由 MFC 提供):```cpp#include <afxwin.h> // 確保包含 MFC 頭文件#include <crtdbg.h>int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow){// ... MFC 初始化 (通常隱含在 AfxWinMain 中) ...CWinApp* pApp = AfxGetApp();if (pApp != nullptr){pApp->InitApplication();pApp->InitInstance();pApp->Run();pApp->ExitInstance(); // MFC 框架調用}// 在 WinMain 返回前,所有全局/靜態對象應已析構#ifdef _DEBUG_CrtDumpMemoryLeaks(); // 在此處檢測#endifreturn 0;}```實際應用中,你通常需要修改 MFC 提供的 `WinMain` 實現(在 `appmodul.cpp` 或類似文件中)。
優點: 時機也相對較晚(在 `ExitInstance` 之后,所有全局/靜態對象析構后,程序真正結束前)。
缺點:MFC 應用程序的入口點通常被框架封裝 (`AfxWinMain`),直接修改它需要找到對應的源文件(如 `appmodul.cpp`)并可能影響項目結構。不如方法 1 方便通用。方法 3: 使用靜態對象析構函數 (高級技巧)原理: 定義一個全局的靜態對象。C++ 保證全局靜態對象在 `main`/`WinMain` 結束后、程序完全退出前析構。在這個靜態對象的析構函數中調用 `_CrtDumpMemoryLeaks`。
位置: 在一個全局靜態對象的析構函數中。
代碼:```cpp// 在你的某個 .cpp 文件中class LeakDetectorFinalizer {public:~LeakDetectorFinalizer() {#ifdef _DEBUG_CrtDumpMemoryLeaks(); // 在靜態對象析構時檢測#endif}};// 定義一個全局靜態實例static LeakDetectorFinalizer s_finalLeakChecker;```
優點: 理論上時機也很晚(在大多數其他全局/靜態對象析構之后)。
缺點:析構順序不確定性: C++ 標準只規定了同一編譯單元內靜態對象析構的逆初始化順序,不同編譯單元間的析構順序是未定義的。如果存在其他全局靜態對象依賴后釋放的資源,且它們在 `s_finalLeakChecker` 之后析構,那么這些對象仍會被誤報為泄漏。這種方法不如方法 1 可靠。略顯晦澀。總結與最佳實踐1.  絕對避免在 `ExitInstance` 和 `CWinApp` 析構函數中調用 `_CrtDumpMemoryLeaks`: 這是誤報的主要根源。
2.  首選方法: 在 `InitInstance` 開頭使用 `_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);`。這是 VC++/MFC 調試內存泄漏的標準、最可靠、誤報最少的方法。它確保了檢測發生在程序生命周期的最后。
3.  替代方法 (謹慎使用):修改 `WinMain`/`main` 在 `return` 前調用:時機正確,但修改 MFC 入口點文件稍顯麻煩。靜態對象析構函數調用:理論上可行,但受限于靜態對象析構順序的不確定性,不推薦作為首選。
4.  僅限 Debug 模式: 務必使用 `#ifdef _DEBUG` 和 `#endif` 將內存泄漏檢測代碼包裹起來,確保它們只在調試版本中編譯和執行。在 Release 版本中檢測不僅無效(因優化可能導致信息不準),還會增加不必要的開銷。
5.  理解輸出: 即使使用最佳方法,有時也會看到一些來自 MFC 內部或第三方庫的“報告”。學習識別這些常見的“偽泄漏”(通常分配號固定)或使用 `_CrtSetBreakAlloc` 配合分配號來精確定位真正的泄漏點。`_CrtMemCheckpoint` 和 `_CrtMemDifference` 可用于在程序關鍵點進行更精細的內存狀態快照比較。核心要點:延遲檢測! 讓 `_CrtDumpMemoryLeaks` 在程序真正結束、所有資源(尤其是全局/靜態對象)都釋放完畢之后再執行,是解決誤報問題的關鍵。`_CrtSetDbgFlag` 方法正是利用了 C 運行時庫自身在退出時的最后清理點來保證這一點。

7、斷點示例(實操)

錯誤太多就分模塊屏蔽

錯誤提示

Detected memory leaks!
Dumping objects ->
{9986} normal block at 0x00000248AB882D40, 1 bytes long.Data: < > CD 
{9978} normal block at 0x00000248AB88F300, 16 bytes long.Data: <    H           > E0 D3 88 AB 48 02 00 00 00 00 00 00 00 00 00 00 

代碼斷點

BOOL X::InitInstance()
{// 啟用自動內存泄漏檢測(調試模式)
#ifdef _DEBUG_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);_CrtSetBreakAlloc(9986);
#endif
……
}

代碼定位問題出處

(自定定義的單例把釋放的new屏蔽掉了導致的)

8、內存泄露實操方案(Hook)

8.1、在自定的.cpp文件加上

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

?8.2 使用hook

#ifdef _NewLeak
int __cdecl AllocHook(int allocType, void* userData, size_t size,int blockType, long requestNumber, const unsigned char* filename, int lineNumber) {char buffer[512];if (requestNumber > 0) // 屏蔽掉分配的{sprintf_s(buffer, sizeof(buffer),"分配序號: %ld, 大小: %zu, 文件: %s, 行號: %d\n",requestNumber, size, filename ? (const char*)filename : "未知", lineNumber);OutputDebugStringA(buffer);}return TRUE;
}
#endifBOOL X::InitInstance()
{// 啟用自動內存泄漏檢測(調試模式)
#ifdef _NewLeak_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);_CrtSetAllocHook(AllocHook);
#endif
……
}

8.3 可以在自己new的代碼打個斷點執行完畢即可看到? 輸出框 職中的顯示
9、內存泄露實操
分段屏蔽,逐步縮小內存泄露范圍

比如經過屏蔽測試

?m_capture.open(0); 會導致內存泄露

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/84332.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/84332.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/84332.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于區塊鏈的供應鏈溯源系統:構建與實踐

前言 在當今全球化的經濟環境中&#xff0c;供應鏈的復雜性不斷增加&#xff0c;商品從原材料采購到最終交付給消費者的過程涉及多個環節和眾多參與者。如何確保供應鏈的透明度、可追溯性和安全性&#xff0c;成為企業和消費者關注的焦點。區塊鏈技術以其去中心化、不可篡改和透…

Web攻防-SQL注入數據格式參數類型JSONXML編碼加密符號閉合

知識點&#xff1a; 1、Web攻防-SQL注入-參數類型&參數格式 2、Web攻防-SQL注入-XML&JSON&BASE64等 3、Web攻防-SQL注入-數字字符搜索等符號繞過 案例說明&#xff1a; 在應用中&#xff0c;存在參數值為數字&#xff0c;字符時&#xff0c;符號的介入&#xff0c…

探秘鴻蒙 HarmonyOS NEXT:實戰用 CodeGenie 構建鴻蒙應用頁面

在開發鴻蒙應用時&#xff0c;你是否也曾為一個頁面的布局反復調整&#xff1f;是否還在為查 API、寫模板代碼而浪費大量時間&#xff1f;今天帶大家實戰體驗一下鴻蒙官方的 AI 編程助手——CodeGenie&#xff08;代碼精靈&#xff09; &#xff0c;如何從 0 到 1 快速構建一個…

DBAPI如何優雅的獲取單條數據

API如何優雅的獲取單條數據 案例一 對于查詢類API&#xff0c;查詢的是單條數據&#xff0c;比如根據主鍵ID查詢用戶信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默認返回的數據格式是多條的&#xff0c;如下&#xff1a; {&qu…

使用Whisper本地部署實現香港版粵語+英語混合語音轉文字方案

今天要一個非常好的朋友有個工作&#xff0c;就是要把醫院醫生診斷的說話記錄轉成文字&#xff0c;之前都是她本人一句一句的聽&#xff0c;然后記錄下來的&#xff0c;我想通過ai 來解決這個問題。 她的需求如下&#xff1a; 不能把數據傳到網上&#xff0c;隱私問題所以需要…

案例分享--汽車制動卡鉗DIC測量

制動系統是汽車的主要組成部分&#xff0c;是汽車的主要安全部件之一。隨著車輛性能的不斷提高&#xff0c;車速不斷提升&#xff0c;對車輛的制動系統也隨之提出了更高要求&#xff0c;因此了解車輛制動系統中每個部件的動態行為成為了制動系統優化的主要途徑&#xff0c;同時…

保姆級教程:在無網絡無顯卡的Windows電腦的vscode本地部署deepseek

文章目錄 1 前言2 部署流程2.1 準備工作2.2 Ollama2.2.1 使用有網絡的電腦下載Ollama2.2.2 安裝Ollama&#xff08;有網絡的電腦&#xff09;2.2.3 安裝Ollama&#xff08;無網絡的電腦&#xff09;2.2.4 安裝驗證2.2.5 修改大模型安裝位置2.2.6 下載Deepseek模型 2.3 將deepse…

【Redis技術進階之路】「原理分析系列開篇」分析客戶端和服務端網絡誦信交互實現(服務端執行命令請求的過程 - 初始化服務器)

服務端執行命令請求的過程 【專欄簡介】【技術大綱】【專欄目標】【目標人群】1. Redis愛好者與社區成員2. 后端開發和系統架構師3. 計算機專業的本科生及研究生 初始化服務器1. 初始化服務器狀態結構初始化RedisServer變量 2. 加載相關系統配置和用戶配置參數定制化配置參數案…

VB.net復制Ntag213卡寫入UID

本示例使用的發卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、讀取舊Ntag卡的UID和數據 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click輕松讀卡技術支持:網站:Dim i, j As IntegerDim cardidhex, …

SQL SERVER 數據庫遷移的三種方法!

要將SQL Server從研發環境的把數據庫結構(不含數據)遷移至生產環境,可通過以下幾種方法實現。以下是具體操作步驟及適用場景: ?? 一、使用SSMS圖形界面生成結構腳本(推薦新手) 通過SQL Server Management Studio的生成腳本向導,僅導出數據庫架構: ??連接測試庫??…

C# 快速檢測 PDF 是否加密,并驗證正確密碼

引言&#xff1a;為什么需要檢測PDF加密狀態&#xff1f; 在批量文檔處理系統&#xff08;如 OCR 文字識別、內容提取、格式轉換&#xff09;中&#xff0c;加密 PDF 無法直接操作。檢測加密狀態可提前篩選文件&#xff0c;避免流程因密碼驗證失敗而中斷。 本文使用 Free Spire…

(33)課54:3 張表的 join-on 連接舉例,多表查詢總結。數據庫編程補述及游標綜合例題。靜態 sqL與動態sqL(可帶參數)

&#xff08;112&#xff09;3 張表的 join-on 連接舉例 &#xff1a; &#xff08;113&#xff09; 多表查詢總結 &#xff1a; &#xff08;114&#xff09;數據庫編程補述 &#xff1a; 綜合例題 &#xff1a; 以上沒有動手練習&#xff0c;不知道這樣的語法是否…

再見 Navicat!一款開源的 Web 數據庫管理工具!

大家好&#xff0c;我是 Java陳序員。 在日常的開發工作中&#xff0c;常常需要與各種數據庫打交道。而為了提高工作效率&#xff0c;常常會使用一些可視化工具進行操作數據庫。 今天&#xff0c;給大家介紹一款開源的數據庫管理工具&#xff0c;無需下載安裝軟件&#xff0c…

OkHttp 中實現斷點續傳 demo

在 OkHttp 中實現斷點續傳主要通過以下步驟完成&#xff0c;核心是利用 HTTP 協議的 Range 請求頭指定下載范圍&#xff1a; 實現原理 Range 請求頭&#xff1a;向服務器請求文件的特定字節范圍&#xff08;如 Range: bytes1024-&#xff09; 本地文件記錄&#xff1a;保存已…

函數中的Callable

在編程中&#xff0c;?Callable&#xff08;可調用對象&#xff09;?? 是指任何可以通過 () 操作符調用的對象。在函數和類設計的上下文中&#xff0c;Callable 通常指代可以被調用的實體&#xff0c;例如函數、方法、Lambda表達式或實現了 __call__ 方法的對象。以下是詳細…

MySQL學習之觸發器

文章目錄 前言什么是觸發器&#xff08;Trigger&#xff09;&#xff1f;觸發器的特點 MySQL中觸發器的用法創建NEW 與 OLD舉例其他操作 注意事項后續內容參考目錄 前言 閱讀本文前請注意最后編輯時間&#xff0c;文章內容可能與目前最新的技術發展情況相去甚遠。歡迎各位評論…

AIGC 基礎篇 Python基礎 04 for循環與while循環

今天&#xff0c;我們來講Python里面的循環部分 1.for循環 for i in range(1,10,2):print(i) 這是一個簡單但是完整的for循環&#xff0c;里面包含了for循環的所有結構 首先注意格式是“for 變量 in range(取得到的起始值&#xff0c;取不到的終點值&#xff0c;步長)” …

安寶特方案丨XRSOP人員作業標準化管理平臺:AR智慧點檢驗收套件

在選煤廠、化工廠、鋼鐵廠等過程生產型企業&#xff0c;其生產設備的運行效率和非計劃停機對工業制造效益有較大影響。 隨著企業自動化和智能化建設的推進&#xff0c;需提前預防假檢、錯檢、漏檢&#xff0c;推動智慧生產運維系統數據的流動和現場賦能應用。同時&#xff0c;…

每日算法 -【Swift 算法】三數之和最接近目標值

?? Swift 實現:三數之和最接近目標值(3Sum Closest) ? 前言 在算法學習過程中,經典的“三數之和”系列題目是很多人通往進階路上的一道坎。今天我們來介紹其中一個非常實用的變種問題 —— 三數之和最接近目標值(3Sum Closest),并使用 Swift 實現一個高效的解法。 …

python打卡day50@浙大疏錦行

知識點回顧&#xff1a; resnet結構解析CBAM放置位置的思考針對預訓練模型的訓練策略 差異化學習率三階段微調 ps&#xff1a;今日的代碼訓練時長較長&#xff0c;3080ti大概需要40min的訓練時長 作業&#xff1a; 好好理解下resnet18的模型結構嘗試對vgg16cbam進行微調策略 R…