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_DF | OFF | 打开:在程序退出时通过对 _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); 会导致内存泄露
相关文章:

MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...

【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...

23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...

遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...

Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...

大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...

循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的一体化测试平台,覆盖应用全生命周期测试需求,主要提供五大核心能力: 测试类型检测目标关键指标功能体验基…...
在rocky linux 9.5上在线安装 docker
前面是指南,后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...

Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...
mongodb源码分析session执行handleRequest命令find过程
mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程,并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令,把数据流转换成Message,状态转变流程是:State::Created 》 St…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件
在选煤厂、化工厂、钢铁厂等过程生产型企业,其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进,需提前预防假检、错检、漏检,推动智慧生产运维系统数据的流动和现场赋能应用。同时,…...

如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...