vs2019 - detected memory leak

文章目录

    • 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-libraryint main()
{_CrtMemState s1, s2, s3;_CrtMemCheckpoint(&s1);// Memory snapshot will be taken at this pointstd::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
#endifvoid 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// Cexp005MfcDlgAppBEGIN_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 beginchar* 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.hqwc.cn/news/624192.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

商家转账到零钱全攻略:开通、使用、区别与常见问题解答

商家转账到零钱是什么&#xff1f; 【商家转账到零钱】可以说是【企业付款到零钱】的升级版&#xff0c;商家转账到零钱可以为商户提供同时向多个用户微信零钱转账的能力&#xff0c;支持分销返佣、佣金报酬、企业报销、企业补贴、服务款项、采购货款等自动向用户转账的场景。…

ctf.show_web13

上传一句话木马 1.php文件&#xff0c;显示 再改后缀为.jpg&#xff0c;显示错误文件大小 用dirsearch扫一下 备份文件.bak 下载文件源码 <?php header("content-type:text/html;charsetutf-8");$filename $_FILES[file][name];$temp_name $_FILES[file][tm…

fork()的一道面试题

前言&#xff1a;题源 #include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <sys/wait.h> int main(void) {int i;for(i0; i<2; i){fork();printf("-");}wait(NULL);wait(NULL);return 0; }知道一点fork()这个系统…

C++:Static

1 在内存中所在的位置 回想C程序中内存的划分是什么呢&#xff1f;从上到下首先是内核空间&#xff0c;然后是栈内存&#xff0c;内存映射段&#xff0c;堆&#xff0c;数据段&#xff0c;和代码段。用一张图的解释就是&#xff1a; 在这里主要探讨static&#xff0c;因此主要…

C语言中原码,反码,补码与移位操作符

前言 我们现在学习一下C语言中移位操作符的使用&#xff0c;与原码、补码、反码的概念与使用&#xff0c;在原码补码反码中&#xff0c;正整数三个都相同&#xff0c;负数的话我们在下面详细讲解。 原码反码补码的概念&#xff1a;他们是整数的二进制表示的三种方式 正整数 在正…

如何降低漏测, 避免上线后出bug,6年测试心得分享

一、漏测原因总结 &#xff08;1&#xff09;需求评审质量低&#xff0c;需求设计简单、只是简单描述功能&#xff0c;功能逻辑较少   &#xff08;2&#xff09;需求变更频繁   &#xff08;3&#xff09;缺少需求分解&#xff08;sql 文档、用例设计&#xff09;   &…

爬取东方财富股票代码

我们打开东方财富网站&#xff1a;http://quote.eastmoney.com/stocklist.html 假如懒得爬&#xff0c;也可以用现成的股票数据源&#xff1a;https://stockapi.com.cn 这展示了所有股票信息&#xff0c;不过需要我们分页去爬取 我们可以查询具体的html代码&#xff1a; <…

考研数学|《1800》《1000》《660》《880》如何搭配❓

这几本书都是不同阶段对应的习题册 我觉得最舒服的使用就是方式就是基础阶段用《1800题基础部分》然后强化阶段主要刷《880题》并且强化阶段带着刷《660题》 上面是我的使用方式。之所以没有刷《1000题》是因为这本习题册的难度对我来说还是太大了&#xff0c;并且计算量很大…

LeetCode 每日一题 Day 123-136

1379. 找出克隆二叉树中的相同节点 给你两棵二叉树&#xff0c;原始树 original 和克隆树 cloned&#xff0c;以及一个位于原始树 original 中的目标节点 target。 其中&#xff0c;克隆树 cloned 是原始树 original 的一个 副本 。 请找出在树 cloned 中&#xff0c;与 tar…

【Python基础】—— scipy.spatial.KDTree、matplotlib.pyplot、imageio

scipy.spatial参考博客&#xff1a;Python点云处理——建立KDtree 1 KDtree算法原理 KDtree构建出了一种类似于二叉树的树形数据存储结构&#xff0c;每一层都对应原始数据中相应的维度&#xff0c;以K层为一个循环&#xff0c;因此被称为KDtree。 每一层的左右子树的划分依据…

PLC扩展更自由,钡铼IOy系列模块实现DI/DO/AI/AO任意组合

随着工业自动化的不断发展&#xff0c;PLC&#xff08;可编程逻辑控制器&#xff09;作为工业控制领域的核心设备&#xff0c;扮演着至关重要的角色。而钡铼IOy系列模块作为PLC的重要扩展设备&#xff0c;不仅实现了DI&#xff08;数字输入&#xff09;、DO&#xff08;数字输出…

Noisy Student(CVPR 2020)论文解读

paper&#xff1a;Self-training with Noisy Student improves ImageNet classification official implementation&#xff1a;https://github.com/google-research/noisystudent 本文的创新点 本文提出了一种新的半监督方法Noisy Student Training&#xff0c;主要包括三步…