内存泄漏检测

简介

具有动态的分配和释放内存的能力是C/C++程序语言的重要特色之一,但是中国的哲人孙子指出,最强有力的也是最脆弱的。对C/C++应用程序来说这当然是正确的,内存管理错误通常是bug起源之一。非常微妙且难于检测的bug之一就是内存泄漏——不能正确地去分配已经分配了的内存。一个仅仅发生一次的轻微内存泄漏不可能引起注意,但是泄漏了大量内存或者日益增多的泄漏的程序可能表现出征兆,从可怜的(和慢慢地减少)性能到内存不足而完全失灵。更坏的是,一个有泄漏的程序可能占用很多的内存以至于导致另一个程序失灵,留给用户的只是对问题的一无所知。此外,一个严重的内存泄漏甚至可能是其他问题的征兆。

幸运的是,Visual C++ debugger 和 CRT库提供给你一系列有效的检测和鉴定内存泄漏的工具。这片文章阐述了如何使用这些工具去有效并系统的隔离内存泄漏。

设置内存泄漏检测

检测内存泄漏的基本工具是调试器和CRT调试堆函数。为了使用调试堆函数,在你的程序中你必须含有下面的说明:

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

#include说明必须按顺序说明。如果你改变了顺序,你所用的函数可能不能正常工作。包含crtdbg.h的_malloc_dbg_free_dbgmallocfree函数映射到测试版中,它可以跟踪内存的分配和释放。这种映射仅仅在一个测试体系中发生(也就是说,仅仅当_DEBUG被定义的时候)。释放的体系使用通常的mallocfree功能。

#define说明映射CRT堆函数的低级版本到相应的测试版本。这个说明是不需要的,但是没有它,内存泄漏处含有的只是没有多大用处的信息。

一旦你已经增加了刚才的说明,你能够通过在你的程序中包含下面的说明来释放内存信息:

_CrtDumpMemoryLeaks();

当你在调试情况下运行你的程序时,在输出窗口的Debug 标签处_CrtDumpMemoryLeaks表现出内存泄漏的信息。内存泄漏信息类似下面这样:

Detected memory leaks!

Dumping objects ->

C:/PROGRAM FILES/VISUAL STUDIO/MyProjects/leaktest/leaktest.cpp(20) : {18} normal block at 0x00780E80, 64 bytes long.

Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD

Object dump complete.

如果你没有用#define _CRTDBG_MAP_ALLOC说明,内存漏洞堆存处类似下面这样:

Detected memory leaks!

Dumping objects ->

{18} normal block at 0x00780E80, 64 bytes long.

Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD

Object dump complete.

像你所知道的,当_CRTDBG_MAP_ALLOC被定义时,_CrtDumpMemoryLeaks给了你更多的有用信息。如果_CRTDBG_MAP_ALLOC没有被定义,那么将向你如下显示:

  • 内存分配数值(花括号内)
  • 模块的类型(normal、client或者CRT)
  • 以十六进制格式定位的内存
  • 以字节计模块的大小
  • 第一个十六字节的内容(也可以用十六进制)

当定义了_CRTDBG_MAP_ALLOC的时候,显示的内容也向你展现了出现泄漏内存所分配地方的文件。在文件名之后括号内的数字(20,以此为例)是文件内的行数值。如果你双击包含行数值和文件名的输出行,

C:/PROGRAM FILES/VISUAL STUDIO/MyProjects/leaktest/leaktest.cpp(20) : {18} normal block at 0x00780E80, 64 bytes long.

指针将会跳到源文件中内存被分配地方的行(在上面的情况下,leaktest.cpp的行号为20)。选择输出行并按F4将有同样的效果。

使用_CrtSetDbgFlag

如果你的程序总是在同一各地方存在,那么调用_CrtDumpMemoryLeaks时非常容易的。但是,如果你的程序需要在多个位置退出该怎么办?在每一个可能的出口处如果不调用_CrtDumpMemoryLeaks,你可在你的程序开始处包含下面的调用:

_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

当你的程序退出时,这个说明自动地调用_CrtDumpMemoryLeaks。你必须设置两个位域,_CRTDBG_ALLOC_MEM_DF_CRTDBG_LEAK_CHECK_DF,像以前说明的一样。

翻译内存模块的类型

像早期声明的一样,内存泄漏信息鉴别泄漏内存的每一个模块作为一个普通的模块、一个客户模块或者一个CRT模块。实际上,普通的模块和客户模块是你可能留心的唯一类型。

  • 一个普通模块(normal block)是由你的程序分配的普通内存。
  • 一个客户模块(client block)是一种特殊的内存模块,它由于需要一个析构函数的对象而被Microsoft Foundation Classes (MFC)所使用。MFC new操作子建立一个普通模块或者一个客户模块,来适合被创建的模块。
  • 一个CTR模块是由CRT库提供自己使用而分配的内存模块。CRT库对这些模块来管理自己的去分配,因此你不可能在内存泄漏报告中注意到这些,除非有些地方有严重的错误(例如,CRT库崩溃)。

在内存泄漏信息中有两种你从来没有见过的模块类型:

  • 空闲模块(free block)是一种被释放的内存模块
  • Ignore block是你已经特殊标记过以至于在内存泄漏报告中不会出现的模块。

设置CRT报告样式

像以前描写的一样,按默认方式,_CrtDumpMemoryLeaks倾卸内存泄漏信息到输出窗口的Debug窗格。你可以运用_CrtSetReportMode重新设置它到堆存处,到另一个位置。如果你使用一个库,它可能重新设置输出到另一个位置。在这种情况下,你能够利用下面的说明来设置输出位置回到输出窗口:

_CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_DEBUG );

关于使用_CrtSetReportMode去发送输出信息到另一个位置,要看Visual C++文件的_CrtSetReportMode节。

在内存分配数目处设置一个断点

在内存泄漏报告中的文件名和行号可告诉你泄漏的内存在那里被分配,但是了解内存在那里分配对于鉴定问题不总是充分的。在一个程序运行过程中,经常是一个分配将会被调用很多次,但是它可能在某次调用中泄漏内存。为了确定问题,你必须不但知道泄漏的内存在那里分配,还要知道泄漏发生的条件。对你来说,使它成为可能的那条信息是内存分配号。当那些被显示的时候,文件名和行号之后,这是在curly brace中出现的数值。例如,在下面的输出中,“18”是内存分配号。它的意思是泄漏的内存是你程序中内存分配的第十八个模块。

Detected memory leaks!

Dumping objects ->

C:/PROGRAM FILES/VISUAL STUDIO/MyProjects/leaktest/leaktest.cpp(20) : {18} normal block at 0x00780E80, 64 bytes long.

Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD

Object dump complete.

CRT库计算在程序运行期间分配的所用内存模块,包括CRT自己分配的内存或者诸如MFC的其它模块。因此带有分配号n的一个对象是在你的程序中分配的第n个对象,但不可能是由代码分配的第n个对象。(在大部分情况下,它是不会的。)

你可以利用分配号在内存分配的地方设置一个断点。为了做这些,你可以距离你的程序开始很近处,设置一个位置断点。当你的程序在那一点暂停时,你能够从QuickWatch对话框或者Watch窗口设置这样一个位置断点。例如,在Watch窗口中,在Name栏键入下面的表达式:

_crtBreakAlloc

如果你正在用CRT库的多线程的dynamic-link library (DLL)版本,你必须含有上下文操作符,像这里说明的:

{,,msvcrtd.dll}_crtBreakAlloc

现在,按RETURN。调试器评估调用并且把结果放置在Value栏。如果你在内存分配过程中还没有设置任何断点,那么这个值是-1。使用你想中断处内存分配的分配数值来代替Value表中的值——例如,18 去中断早期在输出过程中展现的分配.

当你在你感兴趣的内存分配处设置断点之后,你能够继续调试。在与从前相同的条件下,运行程序时一定要小心,因而分配的顺序不会改变。当你的程序在一个特殊的内存分配点中断的时候,你能够查看Call Stack窗口和其他的测试信息来确定在此条件下内存的分配。如果需要的话,你可以继续从那一点执行程序,以至于了解对象到底发生了什么事,同时还可能确

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目前有一款常用的内存泄漏检测工具是Visual Leak Detector(VLD)。 VLD是一款免费的内存泄漏检测工具,适用于Visual C环境。它能够为每个泄漏的块提供完整的堆栈跟踪信息,包括源文件和行号。同时,VLD可以检测大多数类型的进程内内存泄漏,包括基于COM的泄漏和纯Win32基于堆的泄漏。此外,VLD还支持设置过滤指定的模块,不参与内存泄漏检查,并提供泄漏块的完整数据转储。你可以将泄漏报告保存到文件或发送到调试器,并可以设置不同的详细级别。VLD是一个已经打包的库,使用时无需编译其源代码,只需引入头文件即可。它的源代码使用GNU许可发布,并有详尽的文档和注释,适合想深入了解堆内存管理的读者使用。 [2] 另外,还有一种内存泄漏检测工具是QT内置的功能。当一个QObject正在接受事件队列时,如果中途被销毁,可能会导致问题。为了避免这种情况,推荐使用QObject的deleteLater()函数来删除QObject对象。这个函数会发送一个删除事件到事件系统,让所有事件都处理完之后再清除内存。而且,即使多次调用deleteLater()函数也不会出现问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Qt / MSVC 中使用内存泄露检测工具 VLD(Visual Leak Detector)](https://blog.csdn.net/weixin_41863029/article/details/128482295)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Qt内存泄漏总结(包括检测工具)](https://blog.csdn.net/a844651990/article/details/78814076)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值