C++ 中的内存泄露一般指堆中的内存泄露。堆内存是我们手动 malloc/realloc/new 申请的,程序不会自动回收,需要调用 free 或 delete 手动释放,否则就会造成内存泄露。内存泄露其实还应该包括系统资料的泄露,比如 socket 连接等,使用完后也要释放。

内存泄露的原因:

总结下来,内存泄露大概有一下几个原因:

1、编码错误:malloc、realloc、new 申请的内存在堆上,需要手动显示释放,调用 free 或 delete。申请和释放必须成对出现 malloc/realloc 对应 free,new 对应 delete。前者不会运行构造 / 析构函数,后者会。对于 C++ 内置数据类型可能没差别,但是对于自己构造的类,可能在析构函数中释放系统资源或释放内存,所以要对应使用。

2、“无主” 内存:申请内存后,指针指向内存的起始地址,若丢失或修改这个指针,那么申请的内存将丢失且没有释放。

3、异常分支导致资源未释放:程序正常执行没有问题,但是如果遇到异常,正常执行的顺序或分支会被打断,得不到执行。所以在异常处理的代码中,要确保系统资源的释放。

4、隐式内存泄露:程序运行中不断申请内存,但是直到程序结束才释放。有些服务器会申请大量内存作为缓存,或申请大量 Socket 资源作为连接池,这些资源一直占用直到程序退出。服务器运行起来一般持续几个月,不及时释放可能会导致内存耗尽。

5、类的析构函数为非虚函数:析构函数为虚函数,利用多态来调用指针指向对象的析构函数,而不是基类的析构函数。

内存泄露的检测

内存泄露的关键就是记录分配的内存和释放内存的操作,看看能不能匹配。跟踪每一块内存的声明周期,例如:每当申请一块内存后,把指向它的指针加入到 List 中,当释放时,再把对应的指针从 List 中删除,到程序最后检查 List 就可以知道有没有内存泄露了。Window 平台下的 Visual Studio 调试器和 C 运行时(CRT)就是用这个原理来检测内存泄露。

在 VS 中使用时,需加上

#define _CRTDBG_MAP_ALLOC

#include <crtdbg.h>

crtdbg.h 的作用是将 malloc 和 free 函数映射到它们的调试版本_malloc_dbg 和_free_dbg,这两个函数将跟踪内存分配和释放(在 Debug 版本中有效)

_CrtDumpMemoryLeaks();

函数将显示当前内存泄露,也就是说程序运行到此行代码时的内存泄露,所有未销毁的对象都会报出内存泄露,因此要让这个函数尽量放到最后。

例如:

  1. #define _CRTDBG_MAP_ALLOC

  2. #include <crtdbg.h>

  3. #include <iostream>

  4. using namespace std;

  5. int main(int argc,char** argv)

  6. {

  7. char *str1 = NULL;

  8. char *str2 = NULL;

  9. str1=new char[100];

  10. str2=new char[50];

  11. delete str1;

  12. _CrtDumpMemoryLeaks();

  13. return 0;

  14. }

上述代码中,内存申请了两块,但是只释放了一块,运行调试,会在 output 窗口输出:

Dumping objects ->
{136} normal block at 0x00084D70, 50 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。原因是申请内存用的是 new,而刚刚包含头文件和加宏定义是重载了 malloc 函数,并没有重载 new 操作符,所以要自己定义重载 new 操作符才能检测到泄露内存的申请位置。修改如下:

  1. #define _CRTDBG_MAP_ALLOC

  2. #include <crtdbg.h>

  3. #ifdef _DEBUG // 重载 new

  4. #define new  new(_NORMAL_BLOCK, __FILE__, __LINE__)

  5. #endif

  6. #include <iostream>

  7. using namespace std;

  8. int main(int argc,char** argv)

  9. {

  10. char *str1 = NULL;

  11. char *str2 = NULL;

  12. str1=(char*)malloc(100);

  13. str2=new char[50];

  14. _CrtDumpMemoryLeaks();

  15. return 0;

  16. }

运行结果:

Detected memory leaks!
Dumping objects ->
e:\c++\test\ 内存泄露检测 2\main.cpp (13) : {62} normal block at 0x001714F8, 50 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
e:\c++\test\ 内存泄露检测 2\main.cpp (12) : {61} normal block at 0x00171458, 100 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete.

可以看到

main.cpp () 括号里面的数字就是泄露内存的起始位置。那么后面的 {62} normal block at 0x001714F8, 50 bytes long.
代表什么?

大括号 {} 里面的数字表示第几次申请内存操作;0x001714F8 表示泄露内存的起始地址,CD CD 表示泄露内存的内容。

为什么是第 62 次申请内存,因为在初始化操作时也申请了内存。通过这个信息,可以设置断点。调用 long _CrtSetBreakAlloc (long nAllocID) 可以再第 nAllocID 次申请内存是中断,在中断时获取的信息比在程序终止时获取的信息要多,你可以调试,查看变量状态,对函数调用调试分析,解决内存泄露。

block 分为 3 中类型,此处为 normal,表示普通,此外还有 client 表示客户端(专门用于 MFC),CRT 表示运行时(有 CRT 库来管理,一般不会泄露),free 表示已经释放掉的块,igore 表示要忽略的块。

在上面程序中,调用_CrtDumpMemoryLeaks () 来检测内存泄露,如果程序可能在多个地方终止,必须在多个地方调用这个函数,这样比较麻烦,可以在程序起始位置调用_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF),这样无论程序何时终止,都会在终止前调用_CrtDumpMemoryLeaks ()。

除此之外,还可以在某时刻设置检查点,获取当时内存状态的快照。比较不同时刻内存状态的差异。

  1. #define _CRTDBG_MAP_ALLOC

  2. #include <crtdbg.h>

  3. #ifdef _DEBUG // 重载 new

  4. #define new  new(_NORMAL_BLOCK, __FILE__, __LINE__)

  5. #endif

  6. #include <iostream>

  7. using namespace std;

  8. int main(int argc,char** argv)

  9. {

  10. _CrtMemState s1, s2, s3;

  11. char *str1 = NULL;

  12. char *str2 = NULL;

  13. str1=(char*)malloc(100);

  14. _CrtMemCheckpoint( &s1 );// 记录内存快照

  15. _CrtMemDumpStatistics( &s1 );// 输出

  16. str2=new char[50];

  17. _CrtMemCheckpoint( &s2 );

  18. _CrtMemDumpStatistics( &s2 );

  19. if ( _CrtMemDifference( &s3, &s1, &s2) )// 比较 s1 和 s2,把比较结果输出到 s3

  20. _CrtMemDumpStatistics( &s3 );// dump 差异结果

  21. return 0;

  22. }

输出结果为:

0 bytes in 0 Free Blocks.
100 bytes in 1 Normal Blocks.
8434 bytes in 54 CRT Blocks.
0 bytes in 0 Ignore Blocks.
0 bytes in 0 Client Blocks.
Largest number used: 8963 bytes.
Total allocations: 14003 bytes.
0 bytes in 0 Free Blocks.
150 bytes in 2 Normal Blocks.
8434 bytes in 54 CRT Blocks.
0 bytes in 0 Ignore Blocks.
0 bytes in 0 Client Blocks.
Largest number used: 8963 bytes.
Total allocations: 14053 bytes.
0 bytes in 0 Free Blocks.
50 bytes in 1 Normal Blocks.
0 bytes in 0 CRT Blocks.
0 bytes in 0 Ignore Blocks.
0 bytes in 0 Client Blocks.
Largest number used: 0 bytes.
Total allocations: 50 bytes.

也可以用此法更复杂检测内存泄露,例如设置检查点,检查检查点之间的内存泄露。

在 Linux 下也有类似的方法,具体可以参考:http://en.wikipedia.org/wiki/Mtrace

C++ 内存泄露和检测相关推荐

  1. 检测java内存泄露_MAT 检测 Java内存泄露检测

    一.Java内存泄露例子 Vector v = new Vector( 10 ); for ( int i = 1 ;i < 100 ; i ++ ){ Object obj = new Obj ...

  2. C++内存泄露如何检测?

    1.检查是否存在内存泄露 2.检查哪里出现内存泄露 检查内存泄露不能仅仅靠工具,需要了解代码的框架.即使检查出结果显示出现内存泄露,那也有可能是正常的,具体看你的意图了,如果代码是自己写得那应该不成问 ...

  3. Linux之内存泄露的检测工具

    linux背后隐藏着各种各种丰富的宝藏,找到这些工具,学会这些工具,让这些工具更好地服务于我们的项目开发,不仅可以提高工作的效率,而且可以增强个人技术力.所以围绕在MK1阶段和MK23阶段,有哪些工具 ...

  4. C/C++内存管理详解以及内存泄露的检测

    文章目录 前言 一.C/C++内存分布? 1.内存布局示意图: 2.内存存放的数据类型 二.C/C++内存管理 1.C内存管理 2.C++内存管理 三.内存泄漏 1.概念 2.Windows平台下检测 ...

  5. 内存泄露部分检测工具

    1.ccmalloc-Linux和Solaris下对C和C++程序的简单的使用内存泄漏和malloc调试库. 2.Dmalloc-Debug Malloc Library. 3.Electric Fe ...

  6. DevPartner Studio Professional Edition 11 内存泄露检测使用

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! DevP ...

  7. android跨进程读写内存,Android 跨进程内存泄露

    内存泄露的检测和修复一直是每个APP的重点和难点,也有很多文章讲述了如何检测和修复.本篇文章 结合最近开发的项目遇到的实例,讲述下Android Binder导致的内存泄露的一个案例. 发现问题 参与 ...

  8. 内存泄露、内存溢出以及解决方法

    目录(?)[+] 内存泄露是指程序在运行过程中动态申请的内存空间不再使用后没有及时释放,从而很可能导致应用程序内存无线增长.更广义的内存泄露包括未对系统的资源的及时释放,比如句柄等. 内存溢出即用户在 ...

  9. 使用ViwePager显示图片时如何防止内存泄露。

    内存泄露的检测. 1. 在Android Studio中运行你的应用,然后切换到输出窗口的Android tab. 2. 尽情的玩耍你的应用,最好各个功能都用到,如果是Viewpager,则多滑动一些 ...

最新文章

  1. 使用Python,dlib,OpenCV在实时的视频流中进行面部标志检测
  2. NDK JNI 安装与配置(一)(UBUNTU16.04 )
  3. Java项目:学生学科竞赛管理管理系统设计和实现(java+springboot+ssm+maven)
  4. java http close,okhttpclient-close
  5. hdu 5491 The Next(数学模拟)
  6. 光端机是做什么的?光端机的作用主要有哪些?
  7. redis-配置主从-实际操作-over
  8. Spark 入门系列-简介以及生态
  9. Maven本地仓库下载及配置
  10. php fpm listen.owner,php-fpm配置详解
  11. 【用EXCEL编写俄罗斯方块小游戏(基于VBA)】
  12. Spark面试题修改版本
  13. 误删除文件怎么找回呢?
  14. 微信内置浏览器直接下载APP的解决方法
  15. php ajax 考试倒计时,ajax实现在线考试倒计时
  16. C# Excel命名区域(一)-创建命名区域
  17. Keras,今天7岁啦
  18. BUUCTF-[HCTF 2018]admin1
  19. matlab pcm encode,[MATLAB基础] PCM编码及解码
  20. 集群环境下,谁偷走quartz配置的定时任务

热门文章

  1. 大型智慧校园系统源码 智慧校园源码 Android电子班牌源码
  2. Baxter官网资料(查)
  3. VS_MFC:CSerialPort串口通信
  4. 关于开学第一周的总结
  5. 图像处理之卷积和积分运算
  6. 【c++ 复健】从简单的做起,数组和二维数组
  7. python爬取flickr官网上图片
  8. C语言编程练习 2.编写人得票统计程序。设有3个候选人,每次输入一个得票候选人的名字,不考虑弃权情况,要求最后输出各个候选人的得票结果(参加投票人数由程序运行时输入)。
  9. 20155314 2016-2017-2 《Java程序设计》实验三 敏捷开发与XP实践
  10. vue-weex 仿穷游APP