vs2019 - detected memory leak

文章目录

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

c 复制代码
// 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

c 复制代码
// 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

c 复制代码
// MoneyCostParser.cpp: 定义应用程序的类行为。
//

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

#ifdef _DEBUG
#define new DEBUG_NEW // !!!
#endif
c 复制代码
// 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

c 复制代码
// 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

c 复制代码
//! \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

c 复制代码
//! \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后面

c 复制代码
//! \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

c 复制代码
// 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

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

c 复制代码
// 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

相关推荐
weiwei228444 个月前
Windows10使用MSYS2和VS2019编译FFmpeg详解
ffmpeg·音视频·vs2019·msys2
2345VOR5 个月前
【VS2019安装+QT配置】
开发语言·qt·vs2019
Dream_Chaser~7 个月前
【C++初阶】第十一站:list的介绍及使用
c语言·c++·stl·list·vs2019
星尘库9 个月前
Visual Studio 2019重装vs2019打不开.netcore项目
.netcore·visual studio·vs2019
少莫千华1 年前
【VS】NETSDK1045 当前 .NET SDK 不支持将 .NET 6.0 设置为目标。
.net·vs2019·vs2022·netsdk·1045·目标框架
Dream_Chaser~1 年前
【C++初阶】第一站:C++入门基础(下)
c++·笔记·编程语言·vs2019·范围for·内联·auto
SunkingYang1 年前
如何在vs2019及以后版本(如vs2022)上添加 添加ActiveX控件中的MFC类
mfc·vs2019·vs2022·类型库中的mfc类·如何添加activex控件类·从activex控件添加类向导·mfc odbc使用者
Dream_Chaser~1 年前
【C++初阶】第一站:C++入门基础(中)
linux·c语言·开发语言·c++·笔记·vs2019
纬领网络1 年前
vs2019 未能正确加载settingspackage
vs2019·未能正确加载