首页 > 编程知识 正文

c语言为什么要释放内存(使用智能指针错误导致内存泄漏_C 内存泄露问题定位经验案例)

时间:2023-05-06 19:40:56 阅读:123862 作者:4111

百度百科:

存储器泄漏也称为“存储器泄漏”,使用动态存储器分配函数动态扩展的区域在使用后不会被释放,其结果是持续占用存储器单元。 直到程序结束。 (其实,说白了,自从该内存空间用完后就没有回收)就是所谓的内存泄漏。

内存泄漏图像的比喻是“操作系统可以为所有进程提供的存储空间被某个进程榨取”,最终程序运行的时间越长,占用的存储空间就越多,最终耗尽了所有的存储空间,导致所以“内存泄漏”是从操作系统的角度来看的。 这里的存储空间是指虚拟内存大小,取决于为磁盘交换空间设置的大小,而不是物理内存。 程序申请的内存之一,如果没有指针,该内存就会泄露。

在上述说明中,最后的红色字体部分有问题。 指针指向的内存如果未被管理,也会发生内存泄漏。 在程序开发过程中,指针指向的内存管理不正确是内存问题的最主要原因。

正文的一部分来自网络,如果有问题请指出来。

1 .详细说明

内存增长过程如下:

1.1检测方法

1.1.1 .检查容器

在实际项目中,内存消耗在程序运行时不断增加,这并不一定是内存泄漏造成的。 如果代码量不是很大,则必须首先通读程序,以确定代码是否在逻辑上引起内存问题,特别是STL容器是否为clear。 在多线程中,一些容器经常用作全局变量。 建议仔细检查相关容器是否需要clear。 size ) )函数来检查容器的大小、打印和写入日志,以查看容器的大小是否持续增长。 例如:

映射输入映射;

std:cout Intmap.size (; 1.1.2 .检查new、malloc

首先,最好不要直接使用内存工具对齐,而是按模块读取代码,检查new、malloc分配的内存是否全部正确释放。 例如:

(1) int * p=新int;

delete p;

p=空值;

)2) int* p=new int[10];

delete[] p;

p=空值;

malloc也一样。 不明白的请找相关资料。

每个模块检查代码的好处是自己更了解代码。 特别是要维护的代码,往往是别人开发和实现的。 1.1.3 .库的使用

在Windows上:

Visual Studio调试器和c运行时(CRT )库为检测和识别内存泄漏提供了有效的方法。 原理大致如下。 内存分配通过CRT在运行时实现,如果在内存分配时和内存释放时分别记录,则可以在程序结束时通过比较内存分配和内存释放的记录来判断有无内存泄漏。 在vs上启用内存检查的方法如下。

STEP1,程序中包含以下语句。 (#include语句必须按以下顺序: 更改顺序后,使用的函数可能无法正常工作。 ) 1

2

3#define _CRTDBG_MAP_ALLOC

#包含

#包含

通过包括crtdbg.h,将malloc和free函数映射到调试版本,即_malloc_dbg和_free_dbg,这两个函数跟踪内存的分配和释放。 此映射仅在定义了_DEBUG的调试版本中出现。 发行版使用常规的malloc和free函数。

#define语句将CRT堆函数的基本版本映射到相应的" Debug "版本。 这个句子绝对不需要; 但是,如果没有此语句,内存泄漏转储中包含的有用信息将会很少。

STEP2、添加上述语句后,可以通过在程序中包含以下语句来转储内存泄漏信息: 1_CrtDumpMemoryLeaks (;

此时,完整的代码如下。 1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20#define _CRTDBG_MAP_ALLOC

#包含

#包含

#包含

用户名称空间;

语音获取内存(char * p,intnum ) )。

{

p=(char* ) malloc(sizeof ) char ) * num );

}

(intmain(intargc,char** argv ) ) ) ) ) ) ) )。

{

char*str=NULL;

获取内存(str,100 );

出局了

_CrtDumpMemoryLeaks (;

返回0;

}

当在调试器下运行程序时,_CrtDumpMemoryLeaks将在“输出”窗口中显示内存泄漏信息。内存泄漏信息如下所示:

如果没有使用#define _CRTDBG_MAP_ALLOC语句,内存泄漏转储将如下所示:

未定义_CRTDBG_MAP_ALLOC时,所显示的会是:

内存分配编号(在大括号内)。

块类型(普通、客户端或CRT)。

“普通块”是由程序分配的普通内存。

“客户端块”是由MFC程序用于需要析构函数的对象的特殊类型内存块。MFC new操作根据正在创建的对象的需要创建普通块或客户端块。

“CRT块”是由CRT库为自己使用而分配的内存块。CRT库处理这些块的释放,因此不大可能在内存泄漏报告中看到这些块,除非出现严重错误(例如CRT库损坏)。

从不会在内存泄漏信息中看到下面两种块类型:

“可用块”是已释放的内存块。

“忽略块”是您已特别标记的块,因而不出现在内存泄漏报告中。

十六进制形式的内存位置。

以字节为单位的块大小。

前16字节的内容(亦为十六进制)。

定义了_CRTDBG_MAP_ALLOC时,还会显示在其中分配泄漏的内存的文件。文件名后括号中的数字(本示例中为10)是该文件中的行号。

注意:如果程序总是在同一位置退出,调用将非常容易。如果程序从多个位置退出,则无需在每个可能退出的位置放置对_CrtDumpMemoryLeaks的调用,而可以在程序开始处包含以下调用:1_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

该语句在程序退出时自动调用_CrtDumpMemoryLeaks。必须同时设置_CRTDBG_ALLOC_MEM_DF和_CRTDBG_LEAK_CHECK_DF两个位域,如前面所示。

定位具体的内存泄漏地方:

通过上面的方法,我们几乎可以定位到是哪个地方调用内存分配函数malloc和new等,如上例中的GetMemory函数中,即第10行!但是不能定位到,在哪个地方调用GetMemory()导致的内存泄漏,而且在大型项目中可能有很多处调用GetMemory。如何要定位到在哪个地方调用GetMemory导致的内存泄漏?

定位内存泄漏的另一种技术涉及在关键点对应用程序的内存状态拍快照。CRT库提供一种结构类型_CrtMemState,您可用它存储内存状态的快照:1_CrtMemState s1, s2, s3;

若要在给定点对内存状态拍快照,请向函数传递_CrtMemState结构。该函数用当前内存状态的快照填充此结构:1_CrtMemCheckpoint( &s1 );

通过向函数传递_CrtMemState结构,可以在任意点转储该结构的内容:1_CrtMemDumpStatistics( &s1 );

若要确定代码中某一部分是否发生了内存泄漏,可以在该部分之前和之后对内存状态拍快照,然后使用比较这两个状态:1

2

3

4

5_CrtMemCheckpoint( &s1 );

// memory allocations take place here

_CrtMemCheckpoint( &s2 );

if ( _CrtMemDifference( &s3, &s1, &s2) )

_CrtMemDumpStatistics( &s3 );

wwdl,_CrtMemDifference比较两个内存状态(s1和s2),生成这两个状态之间差异的结果(s3)。在程序的开始和结尾放置_CrtMemCheckpoint调用,并使用_CrtMemDifference比较结果,是检查内存泄漏的另一种方法。如果检测到泄漏,则可以使用_CrtMemCheckpoint调用通过二进制搜索技术来划分程序和定位泄漏。

如上面的例子程序我们可以这样来定位确切的调用GetMemory的地方:1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22#define _CRTDBG_MAP_ALLOC

#include

#include

#include

using namespace std;

_CrtMemState s1, s2, s3;

void GetMemory(char *p, int num)

{

p = (char*)malloc(sizeof(char) * num);

}

int main(int argc,char** argv)

{

_CrtMemCheckpoint( &s1 );

char *str = NULL;

GetMemory(str, 100);

_CrtMemCheckpoint( &s2 );

if ( _CrtMemDifference( &s3, &s1, &s2) )

_CrtMemDumpStatistics( &s3 );

cout<

_CrtDumpMemoryLeaks();

return 0;

}

调试时,程序输出如下结果:

这说明在s1和s2之间存在内存泄漏!!!如果GetMemory不是在s1和s2之间调用,那么就不会有信息输出。

Linux下内存检查:

在上面我们介绍了,vs中在代码中“包含crtdbg.h,将和函数映射到它们的调试版本,即和,这两个函数将跟踪内存分配和释放。此映射只在调试版本(在其中定义了_DEBUG)中发生。发布版本使用普通的malloc和free函数。”即为malloc和free做了钩子,用于记录内存分配信息。

#include

#include

int main(void) {

mtrace(); /* Starts the recording of memory allocations and releases */

int* a = NULL;

a = malloc(sizeof(int)); /* allocate memory and assign it to the pointer */

if (a == NULL) {

return 1; /* error */

}

free(a); /* we free the memory we allocated so we don't have leaks */

muntrace();

return 0; /* exit */

}1.1.4.使用工具

Windows下:Visual Leak Detector

VLD网址:http://vld.codeplex.com/

http://www.codeproject.com/Articles/9815/Visual-Leak-Detector-Enhanced-Memory-Leak-Detectio下载Visual Leak Detector,打开Visual C++ IDE的"工具"→"选项"→"项目和解决方案"→"VC++目录",在"包含文件"中增加VLD的头文件路径"include"路径,在"库文件"增加VLD库文件的"libWin32"路径,此外动态库的"axdsbWin32"路径在安装时已经添加到环境变量里面了,若是未添加,则需要手动拷贝"axdsbWin32"下的文件到可执行文件所在的目录中(拷贝的文件有dbghelp.dll/Microsoft.DTfW.DHL.manifest/vld_x86.dll/vld.ini)。

接下来需要将VLD加入到自己的代码中。方法很简单,只要在包含入口函数的.cpp文件中包含vld.h就可以。如果这个cpp文件中包含了stdafx.h,则将包含vld.h的语句放在stdafx.h的包含语句之后,否则放在最前面。

示例程序:

#include                 //包含VLD的头文件

#include

#include

void f()

{

int *p = new int(0x12345678);

printf("p=%08x, ", p);

}

int main()

{

f();

return 0;

}

注:VLD只能在Windows下使用,在包含vld.h头文件时增加预编译选项。

注:在Release模式下,不会链接VisualLeak Detector。

注:Visual LeakDetector有一些配置项,可以设置内存泄露报告的保存地(文件、调试器),拷贝"Visual Leak Detector"路径下的vld.ini文件到执行文件所在的目录下(在IDE运行的话,则需要拷贝到工程目录下),修改以下项:

ReportFile =.memory_leak_report.txt

ReportTo = both

Linux下:valgrind

命令:yum install valgrind进行安装。

/usr/include/gnu/stubs.h:7:27: error: gnu/stubs-32.h: No such file or directory

make: *** [TrafficInfo.o] Error 1

是缺少需要的程序包解决办法:安装缺少的包:

yum install glibc-devel

命令:valgrind --tool=memcheck ./TestPRTI即可对TestPRTI程序进行内存检测。

如上图所示知道:

==6118== 100 bytes in 1 blocks are definitely lost in loss record 1 of 1

==6118==    at 0x4024F20: malloc (vg_replace_malloc.c:236)

==6118==    by 0x8048724: GetMemory(char*, int) (in /home/netsky/workspace/a.out)

==6118==    by 0x804874E: main (in /home/netsky/workspace/a.out)

是在main中调用了GetMemory导致的内存泄漏,GetMemory中是调用了malloc导致泄漏了100字节的内存。

详细内容请参考

1.2智能指针

智能指针便可以有效缓解这类问题,但并不意味着使用智能指针就不再会产生内存泄露问题。包括:std::auto_ptr、boost::scoped_ptr、boost::shared_ptr、boost::scoped_array、boost::shared_array、boost::weak_ptr、boost::intrusive_ptr。

对于编译器来说,智能指针实际上是一个栈对象,并非指针类型,在栈对象生命期即将结束时,智能指针通过析构函数释放有它管理的堆内存。所有智能指针都重载了“operator->”操作符,直接返回对象的引用,用以操作对象。访问智能指针原来的方法则使用“.”操作符。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。