文章目录
- 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
- 如果看到了内存泄漏,却无法定位到具体代码行
- 在工程中加入调试开始和结束函数
- 备注 - 一个检测内存泄漏的成功例子
- 备注 - 非MFC程序的泄漏检查
- 备注 - 要将内存泄漏的开关函数卡在程序的最开始和最结尾处
- 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
}
备注 - 一个检测内存泄漏的成功例子
当前工程正常操作退出后,发现内存泄漏,如下:
DllMain DLL_PROCESS_DETACH
Detected memory leaks!
Dumping objects ->
D:\a\_work\1\s\src\vctools\VC7Libs\Ship\ATLMFC\Src\MFC\strcore.cpp(156) : {42277} normal block at 0x000001E6834B0490, 40 bytes long.
Data: <` g > 60 C3 83 67 F9 7F 00 00 07 00 00 00 07 00 00 00
D:\a\_work\1\s\src\vctools\VC7Libs\Ship\ATLMFC\Src\MFC\strcore.cpp(156) : {42274} normal block at 0x000001E6834AF3F0, 42 bytes long.
Data: <` g > 60 C3 83 67 F9 7F 00 00 08 00 00 00 08 00 00 00
D:\a\_work\1\s\src\vctools\VC7Libs\Ship\ATLMFC\Src\MFC\strcore.cpp(156) : {42271} normal block at 0x000001E68076CCA0, 64 bytes long.
Data: <` g > 60 C3 83 67 F9 7F 00 00 13 00 00 00 13 00 00 00
D:\a\_work\1\s\src\vctools\VC7Libs\Ship\ATLMFC\Src\MFC\strcore.cpp(156) : {42268} normal block at 0x000001E68347F850, 78 bytes long.
Data: <` g > 60 C3 83 67 F9 7F 00 00 1A 00 00 00 1A 00 00 00
D:\a\_work\1\s\src\vctools\VC7Libs\Ship\ATLMFC\Src\MFC\strcore.cpp(156) : {42265} normal block at 0x000001E68076DA20, 64 bytes long.
Data: <` g > 60 C3 83 67 F9 7F 00 00 13 00 00 00 13 00 00 00
D:\a\_work\1\s\src\vctools\VC7Libs\Ship\ATLMFC\Src\MFC\strcore.cpp(156) : {42262} normal block at 0x000001E6834B0960, 40 bytes long.
Data: <` g > 60 C3 83 67 F9 7F 00 00 07 00 00 00 07 00 00 00
D:\a\_work\1\s\src\vctools\VC7Libs\Ship\ATLMFC\Src\MFC\strcore.cpp(156) : {42259} normal block at 0x000001E68076CE20, 56 bytes long.
Data: <` g > 60 C3 83 67 F9 7F 00 00 0F 00 00 00 0F 00 00 00
D:\a\_work\1\s\src\vctools\VC7Libs\Ship\ATLMFC\Src\MFC\strcore.cpp(156) : {42256} normal block at 0x000001E6834AF460, 50 bytes long.
Data: <` g > 60 C3 83 67 F9 7F 00 00 0C 00 00 00 0C 00 00 00
D:\a\_work\1\s\src\vctools\VC7Libs\Ship\ATLMFC\Src\MFC\strcore.cpp(156) : {42252} normal block at 0x000001E6834AFD20, 42 bytes long.
Data: <` g > 60 C3 83 67 F9 7F 00 00 08 00 00 00 08 00 00 00
可以看到,只能看到有内存泄漏,但是内存泄漏点都在MFC代码内部。
经过前面的实验,应对这种情况心里已经有了哈数。
因为在工程中,又加了一些.cpp. 我开始检查,是否所有的.cpp头部都加了debug new的宏定义。
经过检查,确实后来的一些.cpp确实没加debug new的定义。添加完debug new定义的.cpp头部如下:
//! \file cipher_aes_128_cbc.cpp
#include "pch.h"
#include "cipher/cipher_aes_128_cbc.h"
#include "openssl/evp.h"
#include "openssl/bio.h"
#include "openssl/err.h"
// debug new 加在其他.h的后面
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// 非MFC的debug new 的头定义放在最后
#include "memOpt/my_debug_new_define.h"
再编译工程,这次可以看到有落在自己代码中的内存泄漏了。
“test.exe”(Win32): 已卸载“D:\my_dev\lib\openssl_3d2\bin\my_zlib_1d3.dll”
D:\my_dev\my_local_git_prj\xx\src\common\OSSL\CMemHookRec_v1.1.cpp(148) : atlTraceGeneral - free mem_hook map, g_mem_hook_map.size() = 0, no openssl API call memory leak
DllMain DLL_PROCESS_DETACH
Detected memory leaks!
Dumping objects ->
D:\a\_work\1\s\src\vctools\VC7Libs\Ship\ATLMFC\Src\MFC\strcore.cpp(156) : {42718} normal block at 0x00000225FAB29F20, 40 bytes long.
Data: <` g > 60 C3 83 67 F9 7F 00 00 07 00 00 00 07 00 00 00
D:\a\_work\1\s\src\vctools\VC7Libs\Ship\ATLMFC\Src\MFC\strcore.cpp(156) : {42715} normal block at 0x00000225FAB2A1C0, 42 bytes long.
Data: <` g > 60 C3 83 67 F9 7F 00 00 08 00 00 00 08 00 00 00
D:\a\_work\1\s\src\vctools\VC7Libs\Ship\ATLMFC\Src\MFC\strcore.cpp(156) : {42712} normal block at 0x00000225FD8F65C0, 64 bytes long.
Data: <` g > 60 C3 83 67 F9 7F 00 00 13 00 00 00 13 00 00 00
D:\a\_work\1\s\src\vctools\VC7Libs\Ship\ATLMFC\Src\MFC\strcore.cpp(156) : {42709} normal block at 0x00000225FD80AA90, 78 bytes long.
Data: <` g > 60 C3 83 67 F9 7F 00 00 1A 00 00 00 1A 00 00 00
D:\a\_work\1\s\src\vctools\VC7Libs\Ship\ATLMFC\Src\MFC\strcore.cpp(156) : {42706} normal block at 0x00000225FD8F6BC0, 64 bytes long.
Data: <` g > 60 C3 83 67 F9 7F 00 00 13 00 00 00 13 00 00 00
D:\a\_work\1\s\src\vctools\VC7Libs\Ship\ATLMFC\Src\MFC\strcore.cpp(156) : {42703} normal block at 0x00000225FAB29190, 40 bytes long.
Data: <` g > 60 C3 83 67 F9 7F 00 00 07 00 00 00 07 00 00 00
D:\a\_work\1\s\src\vctools\VC7Libs\Ship\ATLMFC\Src\MFC\strcore.cpp(156) : {42700} normal block at 0x00000225FD8F5DC0, 56 bytes long.
Data: <` g > 60 C3 83 67 F9 7F 00 00 0F 00 00 00 0F 00 00 00
D:\a\_work\1\s\src\vctools\VC7Libs\Ship\ATLMFC\Src\MFC\strcore.cpp(156) : {42697} normal block at 0x00000225FAB2A620, 50 bytes long.
Data: <` g > 60 C3 83 67 F9 7F 00 00 0C 00 00 00 0C 00 00 00
D:\a\_work\1\s\src\vctools\VC7Libs\Ship\ATLMFC\Src\MFC\strcore.cpp(156) : {42693} normal block at 0x00000225FAB29EB0, 42 bytes long.
Data: <` g > 60 C3 83 67 F9 7F 00 00 08 00 00 00 08 00 00 00
D:\my_dev\xx\src\server\CDlgMakeLicense.cpp(2192) : {42692} normal block at 0x00000225FD80A610, 80 bytes long.
Data: <@ 1 8 % > 40 B6 31 19 F6 7F 00 00 38 A6 B2 FA 25 02 00 00
Object dump complete.
虽然还是有很多内存泄漏还是定位在MFC实现中,且双击到不了MFC代码中(其实定位到也没大用).
但是已经有一条内存泄漏定位在自己代码中(最后一条),双击可以定位到自己代码中。
p_InternalVersionInfo = new CInternalVersionInfo(m_csClientAppPathName.GetString(), NULL);
assert(NULL != p_InternalVersionInfo);
定位到自己代码中,这就好说了。这里没释放,那就在函数出口处释放一下。
SAFE_DELETE(p_InternalVersionInfo);
虽然只有一条内存泄漏定位在自己代码中,现在也修复了。其他定位不到自己代码,先这样。
再重新编译,运行,看看内存泄漏情况如何?
“test.exe”(Win32): 已卸载“D:\my_dev\lib\openssl_3d2\bin\my_zlib_1d3.dll”
D:\my_dev\xx\src\common\OSSL\CMemHookRec_v1.1.cpp(148) : atlTraceGeneral - free mem_hook map, g_mem_hook_map.size() = 0, no openssl API call memory leak
DllMain DLL_PROCESS_DETACH
线程 0x25f34 已退出,返回值为 0 (0x0)。
...
线程 0x27498 已退出,返回值为 0 (0x0)。
程序“[0x27338] KrgySpServer.exe”已退出,返回值为 0 (0x0)。
可以看到,已经没有内存泄漏了。
从这个例子中可以看到,只要自己所有的.cpp入口都加了debug new 宏定义,只要出了内存泄漏,就可以定位到。
备注 - 非MFC程序的泄漏检查
需要对malloc(), new() 都重新定义成debug版的才行,否则有的内存泄漏定位不到具体文件/行号,只能看到内存泄漏的大小。
_CRTDBG_MAP_ALLOC 标记要定义在工程最开始,不要定义在预处理器中
// header.h: 标准系统包含文件的包含文件,
// 或特定于项目的包含文件
//
#pragma once
#include "targetver.h"
// _CRTDBG_MAP_ALLOC 要定义在工程中,不能定义在预处理中
// _CRTDBG_MAP_ALLOC 要定义在所有.h的前面
#define _CRTDBG_MAP_ALLOC
#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容
// Windows 头文件
#include <windows.h>
// C 运行时头文件
#include <stdlib.h>
在每个CPP的最上面定义DEBUG宏
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/find-memory-leaks-using-the-crt-library?view=msvc-170
// _CRTDBG_MAP_ALLOC 必须定义在于编译器中, 否则和其他实现冲突(e.g. malloc.h)
#include <cstdlib>
#include <crtdbg.h> // for malloc()的debug版定义
#include <malloc.h>
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/crt-debug-heap-details?view=msvc-170
// for new()的debug版定义
#ifdef _DEBUG
#define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, __LINE__)
#else
#define DEBUG_CLIENTBLOCK
#endif // _DEBUG
#ifdef _DEBUG
#define new DEBUG_CLIENTBLOCK
#endif
在程序的结尾打印泄漏点
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: 在此处放置代码。
#ifdef _CRTDBG_MAP_ALLOC
OutputDebugString(TEXT("_CRTDBG_MAP_ALLOC was define\r\n"));
#else
OutputDebugString(TEXT("_CRTDBG_MAP_ALLOC was not define\r\n"));
#endif
// 初始化全局字符串
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_CASENOMFCDEBUGNEW, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// 执行应用程序初始化:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_CASENOMFCDEBUGNEW));
char* pszBuf = new char[1024];
pszBuf[0] = 't';
int* x = (int*)malloc(sizeof(int));
*x = 666;
MSG msg;
// 主消息循环:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
_CrtSetReportMode(_CRT_WARN, _CRTDBG_REPORT_MODE); // !!
// _CrtDumpMemoryLeaks();
_CrtMemDumpAllObjectsSince(NULL); // !!
return (int) msg.wParam;
}
备注 - 要将内存泄漏的开关函数卡在程序的最开始和最结尾处
否则程序默认加载的DLL没卸载掉,会有内存泄漏不在自己的代码中。
如果卡在程序的最开始和最结尾处,也会有程序框架默认加载的DLL没卸载干净,但是能看出没有内存泄漏。
// CxxApp 构造
CxxApp::CxxApp()
{
new_debug_begin(); // !
vs_build_type_detected(false);
// 支持重新启动管理器
m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART;
}
CxxApp::~CxxApp()
{
new_debug_end(); // !
}
能看到有点内存泄漏,但都是框架默认加载的资源还没卸载干净引起的。可以确定不是自己的代码引起的。
“xxexe”(Win32): 已加载“C:\Windows\System32\cryptbase.dll”。
线程 0x4337c 已退出,返回值为 0 (0x0)。
DllMain DLL_PROCESS_DETACH
D:\my_dev\my_local_git_prj\product\xxV4\src\common\OSSL\CMemHookRec_v1.1.cpp(182) : atlTraceGeneral - free mem_hook map, g_mem_hook_map.size() = 0, no openssl API call memory leak
Dumping objects ->
{334} client block at 0x000001C18BEB1790, subtype c0, 392 bytes long.
D:\a\_work\1\s\src\vctools\VC7Libs\Ship\ATLMFC\Src\MFC\dumpcont.cpp(23) : atlTraceGeneral - a CObject object at $000001C18BEB1790, 392 bytes long
D:\a\_work\1\s\src\vctools\VC7Libs\Ship\ATLMFC\Src\MFC\strcore.cpp(156) : {328} normal block at 0x000001C18BEDC5F0, 98 bytes long.
Data: <` < $ $ > 60 C3 E9 3C FE 7F 00 00 24 00 00 00 24 00 00 00
{315} normal block at 0x000001C18BEDDBD0, 48 bytes long.
Data: < > D0 DB ED 8B C1 01 00 00 D0 DB ED 8B C1 01 00 00
{314} normal block at 0x000001C18BEDB2D0, 16 bytes long.
Data: < K Q > A8 4B A3 51 F7 7F 00 00 00 00 00 00 00 00 00 00
{128} client block at 0x000001C18BECA9A0, subtype c0, 128 bytes long.
D:\a\_work\1\s\src\vctools\VC7Libs\Ship\ATLMFC\Src\MFC\dumpcont.cpp(23) : atlTraceGeneral - a CDynLinkLibrary object at $000001C18BECA9A0, 128 bytes long
Object dump complete.
线程 0x3b0cc 已退出,返回值为 1 (0x1)。
“xxexe”(Win32): 已卸载“C:\Windows\System32\usp10.dll”
“xxexe”(Win32): 已卸载“C:\Windows\System32\msasn1.dll”
“xxexe”(Win32): 已卸载“C:\Windows\System32\wintrust.dll”
“xxexe”(Win32): 已卸载“C:\Windows\System32\msimg32.dll”
“xxexe”(Win32): 已加载“C:\Windows\System32\iertutil.dll”。
“xxexe”(Win32): 已卸载“C:\Windows\System32\iertutil.dll”
“xxexe”(Win32): 已卸载“C:\Windows\System32\wininet.dll”
“xxexe”(Win32): 已卸载“C:\Windows\System32\winmm.dll”
“xxexe”(Win32): 已卸载“C:\Windows\System32\GooglePinyin2.ime”
“xxexe”(Win32): 已卸载“C:\Windows\System32\wbem\wbemsvc.dll”
“xxexe”(Win32): 已卸载“C:\Windows\System32\wbem\wbemprox.dll”
“xxexe”(Win32): 已卸载“C:\Windows\System32\msxml3.dll”
“xxexe”(Win32): 已卸载“C:\Windows\System32\wbemcomn.dll”
“xxexe”(Win32): 已卸载“C:\Windows\System32\wbem\fastprox.dll”
线程 0x44a2c 已退出,返回值为 0 (0x0)。
线程 0x40d44 已退出,返回值为 0 (0x0)。
线程 0x44b88 已退出,返回值为 0 (0x0)。
线程 0x4214c 已退出,返回值为 0 (0x0)。
线程 0x42764 已退出,返回值为 0 (0x0)。
线程 0x43618 已退出,返回值为 0 (0x0)。
线程 0x422b4 已退出,返回值为 0 (0x0)。
线程 0x44b78 已退出,返回值为 0 (0x0)。
程序“[0x40044] xx.exe”已退出,返回值为 0 (0x0)。
当诊断开关函数,卡在程序最开始/最结尾处,如果还有很多的内存泄漏提示,那再查。
如果只看到2~3条内存泄漏指示,那应该不是自己代码引起的。
现在实验结果看,如果是自己代码引起的内存泄漏,泄漏指示都能落到自己的代码行上。
将所有.cpp开头的debug new 都去掉,加上自己的诊断头文件
// MoneyCostParser.cpp: 定义应用程序的类行为。
//
#include "pch.h"
#include "framework.h"
#include "MoneyCostParser.h"
#include "MoneyCostParserDlg.h"
// 去掉框架加的debug new
//#ifdef _DEBUG
//#define new DEBUG_NEW
//#endif
#include "memOpt/my_debug_new_define.h" // 换上自己的debug new 定义
//! \file my_debug_new_define.h
// 这个头文件是new的debug版定义, 要包含到每个需要的.cpp中,不能有头文件多重包含的保护
// my_debug_new_define.h 只能包含到.cpp中,不能包含到其他.h中
// my_debug_new_define.h 要包含到每个.cpp的所有头文件包含的后面, 保证是该.cpp的最后包含的头文件
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/find-memory-leaks-using-the-crt-library?view=msvc-170
// _CRTDBG_MAP_ALLOC 必须定义在于编译器中, 否则和其他实现冲突(e.g. malloc.h)// 现在已经有 包含 my_debug_new_define_begin.h 替代了
#include <crtdbg.h> // for malloc()的debug版定义
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/crt-debug-heap-details?view=msvc-170
// for new()的debug版定义
#ifdef _DEBUG
#ifdef _MFC_VER
#define DEBUG_CLIENTBLOCK new(__FILE__, __LINE__)
#else
#define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, __LINE__)
#endif // _MFC_VER
#define new DEBUG_CLIENTBLOCK
#else
#define DEBUG_CLIENTBLOCK
#endif // _DEBUG
//#define PROG_TYPE_CONSOLE 1
//#define PROG_TYPE_MFC 2
//#define PROG_TYPE_DLL_NO_MFC 3
//#ifdef _DEBUG
// #ifdef DEBUG_NEW
// #undef DEBUG_NEW
// #endif
// #ifndef DEBUG_NEW
// #define DEBUG_NEW new(__FILE__, __LINE__)
// #endif
//#endif
//#if defined(_MFC_VER)
// #define MY_NEW_TYPE PROG_TYPE_MFC
//#elif defined(_CONSOLE)
// #define MY_NEW_TYPE PROG_TYPE_CONSOLE
//#elif (defined(_USRDLL) && (!defined(_MFC_VER)))
// #define MY_NEW_TYPE PROG_TYPE_DLL_NO_MFC
//#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
//#elif (defined MY_NEW_TYPE) && (PROG_TYPE_DLL_NO_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();