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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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…