vs2019 - detected memory leak

news2025/2/1 22:51:41

文章目录

    • vs2019 - detected memory leak
    • 概述
    • 笔记
    • vs2019 console
    • vs2019 MFC Dlg
    • 但是,工程大了之后,VS2019提示的就变了样
    • 整好的内存泄漏侦测头文件和实现
    • my_debug_new_define.h
    • my_debug_new_define.cpp
    • 在所有.cpp文件入口处包含my_debug_new_define.h
    • 包含的细节 - 如果有预编译头文件(e.g. pch.h), 必须包含在pch.h后面
    • 包含的细节 - 如果工程中的.cpp头部已经重定向了new, 要在重定向之后包含my_debug_new_define.h
    • 如果看到了内存泄漏,却无法定位到具体代码行
    • 在工程中加入调试开始和结束函数
    • END

vs2019 - detected memory leak

概述

用VS2019建立的控制台工程, 在调试模式下, 如果出了内存泄漏,是没有提示的。
// 网上的大佬在2010年就给出了解决方法
// \ref https://www.codeproject.com/articles/66324/detecting-memory-leaks-using-the-crtdbg-library

笔记

分别在新建的VS2019 console和MFC Dlg程序中试试, 出现内存泄漏时,让VS2019IDE将内存泄漏点报出来,且能转到具体代码行。

vs2019 console

// exp003_vs2019_console_detect_memory_leak.cpp
#include <iostream>
#include <crtdbg.h>
#define  new new(_CLIENT_BLOCK,__FILE__, __LINE__)

// 新建vs2019控制台工程, 如果有内存泄漏, 默认是不提示的。
// 网上的大佬在2010年就给出了解决方法
// \ref https://www.codeproject.com/articles/66324/detecting-memory-leaks-using-the-crtdbg-library


int main()
{
    _CrtMemState s1, s2, s3;
    _CrtMemCheckpoint(&s1);// Memory snapshot will be taken at this point
    std::cout << "Hello World!\n";

    uint8_t* pBuf = new uint8_t[100];

    _CrtMemCheckpoint(&s2);// Another Memory snapshot will be taken at this point

    // Memory difference which has been allocated but deallocted between s1 and s2 
    // Memory check points will be calculated. 
    if (_CrtMemDifference(&s3, &s2, &s1)) 
    {
        _CrtDumpMemoryLeaks();  //Dumps the memory leak in Debugger Window, if any, between s1 and s2 memeory check points.
    }

    return 0;
}


在这里插入图片描述

vs2019 MFC Dlg

新建后的工程,如果发生内存泄漏,也是有提示的。且能定位到具体代码行。
在这里插入图片描述

但是,工程大了之后,VS2019提示的就变了样

在这里插入图片描述
如果不是新建的工程,而是已经写了一段时间的工程,代码行数上来之后,在debug版的调试模式下,程序结束后,确实能看到IDE提示有内存泄漏。但是,无法直接定位到泄漏点。 这点,在以前工作中遇到的工程中,也遇到过。如果工程大了,出现了内存泄漏,就不那么好找。

工程大了,VS2019IDE大概率都定位不到具体哪些行引起的内存泄漏。

我在查资料之前,自己用笨办法搞定了。用排除法。让执行一个特定操作可以引起内存泄漏,那么就在这个模块中再用排除法去查。
不过,这得是自己写的工程。如果是同事维护的工程或者开源工程,用排除法基本没得搞。

这时该如何排查呢(是否可以让VS2019自动定位到内存泄漏点?)?我就是因为这个场景,才去查如何定位内存泄漏,才找到了网上大佬2010年就提出的解决方法。

但是,网上大佬的方法只适合工程没有hook new的场景。
e.g. MFC向导生成的工程,已经在debug模式下, 将new换成了DEBUG_NEW

// Memory tracking allocation
void* AFX_CDECL operator new(size_t nSize, LPCSTR lpszFileName, int nLine);
#define DEBUG_NEW new(THIS_FILE, __LINE__)

这时,如果再使用crtdbg.h中的new重定向方法,直接编译不过,和MFC的重定向new是冲突的。只能是使用MFC重定向的new
这时再使用_CrtDumpMemoryLeaks,效果和不加是一样的(只能看到有内存泄漏,无法知道内存泄漏精确代码行)

观察了一下发现,在MFC工程中生成的.cpp顶部,都自己重新重定向了new


// MoneyCostParser.cpp: 定义应用程序的类行为。
//

#include "pch.h"
#include "framework.h"
#include "MoneyCostParser.h"
#include "MoneyCostParserDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW // !!!
#endif


// MoneyCostParserDlg.cpp: 实现文件

#include "pch.h"
#include "framework.h"
#include "MoneyCostParser.h"
#include "MoneyCostParserDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW // !!!
#endif

再查看以前用MFC向导生成的类中的.cpp

// MyWorkerThread.cpp: implementation of the CMyWrokerThread class.
//
//

#include "pch.h"
#include <process.h>
#include "CMyWrokerThread.h"

#ifdef _DEBUG
	#undef THIS_FILE // !!!
	static char THIS_FILE[] = __FILE__; // !!!
	#define new DEBUG_NEW // !!!
#endif

再看自己工程,只能看到有内存泄漏,但是不知道具体泄漏行的场景。
最后用手工排除法确定了.cpp, 这个cpp是自己手写的。
最后试了一下,只要在非MFC生成的.cpp(包括自己手写的.cpp,或者是第三方的.cpp)顶部,加上重定向new的代码,就可以实现自动定位内存泄漏行。

如果在VS2019下,看到有内存泄漏的提示,但是无法定位到精确代码行,这时就要检查一下,是否每个.cpp的开头都有new的重定向代码。

如果是想让自己写的.cpp适用于控制台和MFC或者其他平台,就需要include一个头文件(这个头文件不包含条件包含的保护)进来。
然后按照各种平台的编译环境,将new换成debug new.

整好的内存泄漏侦测头文件和实现

my_debug_new_define.h

//! \file my_debug_new_define.h

// 这个头文件是new的debug版定义, 要包含到每个需要的.cpp中,不能有头文件多重包含的保护
// my_debug_new_define.h 只能包含到.cpp中,不能包含到其他.h中

#define PROG_TYPE_CONSOLE 1
#define PROG_TYPE_MFC 2

#if defined(_MFC_VER)
#define MY_NEW_TYPE PROG_TYPE_MFC
#elif defined(_CONSOLE)
#define MY_NEW_TYPE PROG_TYPE_CONSOLE
#else
#error !!! unknown env, please fixed the code for detected memory leak
#endif

// \ref https://learn.microsoft.com/zh-cn/cpp/preprocessor/hash-if-hash-elif-hash-else-and-hash-endif-directives-c-cpp?view=msvc-170
#if (defined MY_NEW_TYPE) && (PROG_TYPE_CONSOLE == MY_NEW_TYPE)
	#include <crtdbg.h>
	#ifdef new
		#undef new
	#endif

	#define  new new(_CLIENT_BLOCK,__FILE__, __LINE__)
#elif (defined MY_NEW_TYPE) && (PROG_TYPE_MFC == MY_NEW_TYPE)
	#ifdef _DEBUG
		#ifdef new
		#undef new
		#endif

		#define new DEBUG_NEW
	#endif
#endif

void new_debug_begin();
void new_debug_end();

my_debug_new_define.cpp

//! \file my_debug_new_define.cpp

#include "pch.h"
#include "my_debug_new_define.h"

#if (defined MY_NEW_TYPE) && (PROG_TYPE_CONSOLE == MY_NEW_TYPE)
static _CrtMemState s1;
static _CrtMemState s2;
static _CrtMemState s3;
void new_debug_begin()
{
	_CrtMemCheckpoint(&s1);// Memory snapshot will be taken at this point
}

void new_debug_end()
{
    _CrtMemCheckpoint(&s2);// Another Memory snapshot will be taken at this point

        // Memory difference which has been allocated but deallocted between s1 and s2 
        // Memory check points will be calculated. 
    if (_CrtMemDifference(&s3, &s2, &s1))
    {
        _CrtDumpMemoryLeaks();  //Dumps the memory leak in Debugger Window, if any, between s1 and s2 memeory check points.
    }
}
#else
void new_debug_begin()
{
}

void new_debug_end()
{
}
#endif

在所有.cpp文件入口处包含my_debug_new_define.h

包含的细节 - 如果有预编译头文件(e.g. pch.h), 必须包含在pch.h后面

//! \file CTest.cpp
//! \note 用VS2019类向导添加的非UI类,是没有DEBUG_NEW定义的

#include "pch.h" // 如果工程中有预编译头文件(e.g. pch.h), 那么.cpp中要首先包含pch.h

#include "my_debug_new_define.h" // <== 如果要包含头文件,都要在pch.h的后面
#include "CTest.h"

int CTest::fn_add(int a, int b)
{
	char* p = new char[0x100]; // 模拟内存泄漏

	return (a + b);
}

包含的细节 - 如果工程中的.cpp头部已经重定向了new, 要在重定向之后包含my_debug_new_define.h


// exp005MfcDlg.cpp: 定义应用程序的类行为。
//

#include "pch.h"
#include "framework.h"
#include "exp005MfcDlg.h"
#include "exp005MfcDlgDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#include "my_debug_new_define.h" // 如果向导生成的代码中已经重定向了new, 需要放到该代码后面,以便重定向new到crtdbg

// Cexp005MfcDlgApp

BEGIN_MESSAGE_MAP(Cexp005MfcDlgApp, CWinApp)
	ON_COMMAND(ID_HELP, &CWinApp::OnHelp)
END_MESSAGE_MAP()

如果看到了内存泄漏,却无法定位到具体代码行

检查一下,是否工程中所有的.cpp头部都包含了my_debug_new_define.h

在工程中加入调试开始和结束函数

// exp005Vs2019Console.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include "CTest.h"
#include "my_debug_new_define.h"

int main()
{
    new_debug_begin(); // debug new begin

    char* p = new char[66];

    CTest obj;

    obj.fn_add(1, 2);

    std::cout << "Hello World!\n";
    new_debug_end(); // debug new end
}

END

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1599944.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深度学习 Lecture 8 决策树

一、决策树模型&#xff08;Decision Tree Model) 椭圆形代表决策节点&#xff08;decison nodes)&#xff0c;矩形节点代表叶节点&#xff08;leaf nodes)&#xff0c;方向上的值代表属性的值&#xff0c; 构建决策树的学习过程&#xff1a; 第一步&#xff1a;决定在根节点…

【切换网络连接后】VMware虚拟机网络配置【局域网通信】

初次安装Linux虚拟机以及切换网络都需要配置虚拟机网络&#xff0c; 从而使得win主机内通过远程连接工具能够连接该虚拟机&#xff0c; 而不是在虚拟机内操作。 本片文章你将了解到网络切换后如何配置虚拟机网络的一些基础操作&#xff0c;以及局域网通信的一些基础知识。 …

一文看懂CRMEB开源商城系统与标准版之间的区别

一直以来&#xff0c;CRMEB开源商城系统和标准版商城系统都在被比较&#xff0c;很多人都会疑问&#xff0c;为什么有免费的开源商城系统&#xff0c;还有那么多人去购买商业版的CRMEB标准版商城系统呢&#xff1f;其实&#xff0c;在纠结到底选哪款系统时&#xff0c;我们不妨…

重磅消息:CnosDB 文档网站升级全新框架啦!

我们很高兴地宣布&#xff0c;CnosDB 文档网站迎来了一次重大升级&#xff01;现在&#xff0c;我们采用了全新的强大的开源文档框架&#xff0c;为用户提供更流畅、更直观的浏览体验。 全新框架带来的优势&#xff1a; 更快速的加载速度&#xff1a;现在您可以更快地访问并查…

运输问题的中转或者转运问题

1、这类问题&#xff0c;不好理解&#xff0c;做个笔记&#xff0c;记录一下。 2、可以参考一下&#xff1a; https://blog.csdn.net/YUNCHOUSHUO/article/details/121660675?spm1001.2014.3001.5506 这个csdn写的还是不错的&#xff0c;推荐。 或者&#xff0c;可以参考 …

飞腾UEFI电源控制选择代码解析

飞腾UEFI电源控制选择代码解析 CPLD 处理方式EC 处理方式注:本文以飞腾UEFI edk-code-4.2.0版本进行说明,如果有朋友需要借鉴,请使用该版本代码。 以D2000打工工具为例,下图打包工具中有选择主板电源管理方式,这里可以选择CPLD、EC、和SE,其中SE代表为X100控制上下电时序…

2024年面试工具篇Postman面试题及答案

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

市场复盘总结 20240416

仅用于记录当天的市场情况&#xff0c;用于统计交易策略的适用情况&#xff0c;以便程序回测 短线核心&#xff1a;不参与任何级别的调整&#xff0c;采用龙空龙模式 一支股票 10%的时候可以操作&#xff0c; 90%的时间适合空仓等待 二进三&#xff1a; 进级率 50% 最常用的二…

面试:sleep 和 wait

一、共同点 wait(),wait(long)和sleep(long)的效果都是让当前线程暂时放弃CPU的使用权&#xff0c;进入阻塞状态 二、不同点 1、方法归属不同 sleep(long)是Thread的静态方法而wait(), wait(long)都是Object的成员方法&#xff0c;每个对象都有 2、醒来的时机不同 执行sleep(l…

whatsapp 语音通话 音频编码(五)

Whatsapp VoiceCall 客户端通过websocket连接到服务器&#xff0c;客户端发起语音通话请求&#xff0c;并且完成必要的协商之后&#xff0c;就可以直接将语音数据发送给服务器&#xff0c;服务器接收到对方的语音数据之后也会通过websocket将语音数据转发给客户端 获取协商秘…

VMware 虚拟机中的 Ubuntu 16.04 设置 USB 连接

VMware 虚拟机中的 Ubuntu 16.04 设置 USB 连接 1. VMware USB Arbitration Service2. 可移动设备 USB 口连接主机3. 虚拟机 -> 可移动设备 -> 连接 (断开与主机的连接)4. 状态栏 -> 断开连接 (连接主机)References 1. VMware USB Arbitration Service 计算机 -> …

探索分布式系统监控zabbix------------自动发现与自动注册

目录 一、部署 zabbix 服务端 二、部署 zabbix 客户端 2.1环境准备 2.2服务端和客户端都配置时间同步 &#xff08;ntp&#xff09; 2.2.1服务端zbx-server 2.2.2服务端zabbix-agent01客户端 2.3客户端配置时区&#xff0c;与服务器保持一致 2.4设置 zabbix 的下载源&…

[Linux]--关于进程控制

进程创建,fork/vfork 在linux中fork函数是非常重要的函数&#xff0c;它从已存在进程中创建一个新进程。新进程为子进程&#xff0c;而原进程为父进程。 #include <unistd.h> pid_t fork(void); 返回值&#xff1a;自进程中返回0&#xff0c;父进程返回子进程id&#x…

服务器Linux搭建NPM私有仓库

服务器Linux搭建NPM私有仓库 环境搭建 安装 nodejs nodejs官网&#xff1a;https://nodejs.org/en/download/package-manager 可以去官网自行下载nodejs的Linux版本&#xff0c;但是出于别的原因考虑&#xff0c;可以使用nvm去下载nodejs这样会切换nodejs也方便。 nvm 这…

苹果电脑启动磁盘是什么意思 苹果电脑磁盘清理软件 mac找不到启动磁盘 启动磁盘没有足够的空间来进行分区

当你一早打开苹果电脑&#xff0c;结果系统突然提示&#xff1a; “启动磁盘已满&#xff0c;需要删除部分文件”。你会怎么办&#xff1f;如果你认为单纯靠清理废纸篓或者删除大型文件就能释放你的启动磁盘上的空间&#xff0c;那就大错特错了。其实苹果启动磁盘的清理技巧有很…

Java复习第十九天学习笔记(Cookie、Session登录),附有道云笔记链接

【有道云笔记】十九 4.7 Cookie、Session登录 https://note.youdao.com/s/VwpxfEim 一、会话技术简介 生活中会话 我&#xff1a; 小张&#xff0c;你会跳小苹果码&#xff1f; 小张&#xff1a; 会&#xff0c;怎么了&#xff1f; 我&#xff1a; 公司年会上要表演节目&a…

张大哥笔记:一个很笨但是能赚钱的方法

曾经有一个学生向南怀瑾先生提问&#xff1a;什么样的人才能轻松赚到钱&#xff1f;他说方法很简单&#xff0c;假设你现在是一个天天卖烧饼油条的小老板&#xff0c;一天两天没有人买&#xff0c;卖不掉就自己吃&#xff0c;但仍然是一心一意做烧饼油条&#xff0c;三四天后就…

数据结构——双向循环链表

目录 前言 一、链表的分类 二、双向循环链表 2.1 开辟新的节点 2.2 链表初始化 2.3 打印链表 2.4 链表的尾插 2.5 链表的头插 2.6 链表的尾删 2.7 链表的头删 2.8 查找链表 2.9 在pos位置之后插入数据 2.10 删除pos位置的数据 三、完整代码实现 四、顺序表和双向…

Redis中的订阅发布和事务(一)

订阅发布 PUBSUB NUMSUB PUBSUB NUMSUB [channel-1 channel-2… channel-n]子命令接受任意多个频道作为输入参数&#xff0c;并返回这些频道的订阅者数量。 这个子命令是通过pubsub_channels字典中找到频道对应的订阅者链表&#xff0c;然后返回订阅者链表的长度来实现的(订阅…

bdf文件导入事件错误情况

先打开脑电再导入事件 数据情况