C/C++程序一般都由程序员自己来控制内存的申请和释放,因此稍有不慎就容易出现内存泄漏,大家平时开发中都用什么方法和手段来检测、调试程序的内存泄漏呢?

2010-11-13 23:50:04

5 Answers

1. 如何发现内存泄漏

ps -aux

2. 静态分析

**2.1 手动检测**
#include  
#include 
#include 
int LeakTest(char * Para)
{
    if(NULL==Para)
    { 
        //local_log("LeakTest Func: empty parameter\n"); 
        return -1; 
    } 
    char * Logmsg = new char[128]; 
    if(NULL == Logmsg)
    { 
        //
        local_log("memeory allocation failed\n");
        return -2; 
    } 
    sprintf(Logmsg,"LeakTest routine exit: '%s'.\n", Para); 
    local_log(Logmsg); return 0; 
} 
int main(int argc,char **argv )
{ 
    char szInit [] = "testcase1";
    LeakTest(szInit);
    return 0; 
}
**2.2 静态代码分析工具** 代码静态扫描和分析的工具比较多,比如 splint, PC-LINT, BEAM 等。因为 BEAM 支持的平台比较多,这以 BEAM 为例,做个简单介绍,其它有类似的处理过程。 BEAM 可以检测四类问题: 没有初始化的变量;废弃的空指针;内存泄漏;冗余计算。而且支持的平台比较多。
#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 
int *p; 
void foo(int a)
{ 
    int b, c; b = 0; 
    if(!p) 
        c = 1; 
    if(c > a) 
        c += p[1]; 
} 
int LeakTest(char * Para)
{ 
    char * Logmsg = new char[128];
    if((Para==NULL)||(Logmsg == NULL))
        return -1;
    sprintf(Logmsg,"LeakTest routine exit: '%s'.\n", Para); 
    return 0; 
} 
int main(int argc,char **argv ) 
{
    char szInit [] = "testcase1"; 
    LeakTest(szInit); 
    return 0; 
}

**2.3 内嵌程序**


可以重载内存分配和释放函数 new 和 delete,然后编写程序定期统计内存的分配和释放,从中找出可能的内存泄漏。或者调用系统函数定期监视程序堆的大小,关键要确定堆的增长是泄漏而不是合理的内存使用。这类方法比较复杂,在这就不给出详细例子了。


3. 动态运行检测

实时检测工具主要有 valgrind, Rational purify 等。


3.1 Valgrind

valgrind 是帮助程序员寻找程序里的 bug 和改进程序性能的工具。程序通过 valgrind 运行时,valgrind 收集各种有用的信息,通过这些信息可以找到程序中潜在的 bug 和性能瓶颈。

Valgrind 现在提供多个工具,其中最重要的是 Memcheck,Cachegrind,Massif 和 Callgrind。Valgrind 是在 Linux 系统下开发应用程序时用于调试内存问题的工具。它尤其擅长发现内存管理的问题,它可以检查程序运行时的内存泄漏问题。其中的 memecheck 工具可以用来寻找 c、c++ 程序中内存管理的错误。可以检查出下列几种内存操作上的错误:


读写已经释放的内存

读写内存块越界(从前或者从后)

使用还未初始化的变量

将无意义的参数传递给系统调用

内存泄漏

请输入列表文字

3.2 Rational purify

Rational Purify 主要针对软件开发过程中难于发现的内存错误、运行时错误。在软件开发过程中自动地发现错误,准确地定位错误,提供完备的错误信息,从而减少了调试时间。同时也是市场上唯一支持多种平台的类似工具,并且可以和很多主流开发工具集成。Purify 可以检查应用的每一个模块,甚至可以查出复杂的多线程或进程应用中的错误。另外不仅可以检查 C/C++,还可以对 Java 或 .NET 中的内存泄漏问题给出报告。


示例代码

#include <unistd.h> char * Logmsg; int LeakTest(char * Para) { if(NULL==Para) { // local_log("LeakTest Func: empty parameter\n"); return -1; } Logmsg = new char[128]; for (int i = 0 ; i < 128; i++) Logmsg[i] = i%64; if(NULL == Logmsg) { // local_log("memeory allocation failed\n"); return -2; } sprintf(Logmsg,"LeakTest routine exit: '%s'.\n", Para); // local_log(Logmsg); return 0; } int main(int argc,char **argv ) { char szInit [] = "testcase1"; int i; LeakTest(szInit); for (i=0; i < 2; i++) { if(i%200 == 0) LeakTest(szInit); sleep(1); } return 0; }

2010-11-14 01:37:57

1."内存泄露"包括堆内存泄露、栈内存泄露。根据内存的类型,又分为:内存申请、释放,句柄的打开与关闭问题。
2.容易忽视的是栈上的内存泄露,严格来讲是申请的内存超过线程栈空间大小(默认为1MB)。栈上的内存(即局部变量)是不需要释放的,函数返回自动出栈(释放)。若某时刻超过线程栈空间大小,造成其它使用栈的地方不能正常工作(如函数调用、SEH等),会使程序崩溃(驱动的话会蓝屏)。所以申请局部变量内存时,不要太大。
3.堆内存申请、释放。主要是要注意申请的地方要记得释放,以及申请、释放函数要配对使用。比如malloc和free、new和delete、BSTR的SysAllocString和SysFreeString。
4.还有一个地方也往往被忽略,"句柄的打开与关闭问题"。这个当积累到一定数量,是很占内存的。可以linux上的加强进程查看工具观察,若handle数量很大或一直在增长,说明有泄漏。windows下则可以使用微软内核套件SysinternalsSuite出品的procexp.exe工具,如下图:
 By 5lulu.com

看到handles栏了吗。

另外,完全可以重载new、delete等方法,实现简单的垃圾回收,很多软件都这么做。这也是<>上推荐的。

2010-11-14 03:12:39

在 Linux 平台可以用 ps 命令,来监视内存的使用,比如下面的命令 (观测指定进程的VSZ值):

ps -aux

1、静态代码分析工具:比如 splint, PC-LINT, BEAM 等。
而BEAM 支持的平台比较多,可以检测四类问题:

  • 没有初始化的变量;
  • 废弃的空指针;
  • 内存泄漏;
  • 冗余计算。

2、内嵌程序
可以重载内存分配和释放函数 new 和 delete,然后编写程序定期统计内存的分配和释放,从中找出可能的内存泄漏。或者调用系统函数定期监视程序堆的大小,关键要确定堆的增长是泄漏而不是合理的内存使用。

3、动态运行检测:工具主要有 valgrind, Rational purify等

 答案:在 Linux 平台中调试 C/C++ 内存泄漏方法

2010-11-14 05:11:23

检测内存泄漏的主要工具是调试器和 C 运行库 (CRT) 调试堆函数。

若要启用调试堆函数,请在程序中包括以下语句:

#define _CRTDBG_MAP_ALLOC
#include
#include
说明

#include 语句必须采用上文所示顺序。 如果更改了顺序,所使用的函数可能无法正常工作。

通过包括 crtdbg.h,将 malloc 和 free 函数映射到它们的调试版本,即 _malloc_dbg 和 _free_dbg,这两个函数将跟踪内存分配和释放。 此映射只在调试版本(在其中定义了 _DEBUG)中发生。发布版本使用普通的 malloc 和 free 函数。

#define 语句将 CRT 堆函数的基版本映射到对应的“Debug”版本。 并非绝对需要该语句;但如果没有该语句,内存泄漏转储包含的有用信息将较少。

在添加了上述语句之后,可以通过在程序中包括以下语句(通常应恰好放在程序退出位置之前)来转储内存泄漏信息:

_CrtDumpMemoryLeaks();
当在调试器下运行程序时,_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 时,所显示的会是:

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

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

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

以字节为单位的块大小。

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

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

转到源文件中分配内存的行

在“输出”窗口中双击包含文件名和行号的行。

- 或 -

在“输出”窗口中选择包含文件名和行号的行,然后按 F4。

_CrtSetDbgFlag


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


复制

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

设置 CRT 报告模式


默认情况下,_CrtDumpMemoryLeaks 将内存泄漏信息转储到“输出”窗口的“调试”窗格,如上所述。 可以使用 _CrtSetReportMode 重置该设置,以转储到另一位置。

如果使用库,它可以将输出重置到另一位置。

2010-11-14 06:59:28

上面的大侠说的很全面哦。
推荐一个工具:valgrind
推荐一本书: Debug Hacks中文版—深入调试的技术和工具

2010-11-14 08:31:14
您不能回答该问题或者回答已经关闭!

相关文章推荐

  • C#实例解析适配器设计模式

    将一个类的接口变成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够一起工作

  • C#中using指令的几种用法

    using + 命名空间名字,这样可以在程序中直接用命令空间中的类型,而不必指定类型的详细命名空间,类似于Java的import,这个功能也是最常用的,几乎每个cs的程序都会用到

  • C#开发高性能Log Help类设计开发

    项目中要在操作数据库的异常处理中加入写Log日志,对于商业上有要求,写log时对其它操作尽可能影响小,不能因为加入log导致耗时太多

  • C#运行时相互关系

    C#运行时相互关系,包括运行时类型、对象、线程栈和托管堆之间的相互关系,静态方法、实例方法和虚方法的区别等等

  • C#基础概念之延迟加载

    延迟加载(lazy load)是Hibernate3关联关系对象默认的加载方式,延迟加载机制是为了避免一些无谓的性能开销而提出来的,所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作

  • C#协变和逆变

    “协变”是指能够使用与原始指定的派生类型相比,派生程度更大的类型,“逆变”则是指能够使用派生程度更小的类型

  • 使用托管C++粘合C#和C++代码(二)

    本文实现一下C++代码调用C#代码的过程。我构造一个简单并且直观的例子:通过C++ UI 触发C# UI.

  • C#中的索引器的简单理解和用法

    C#中的类成员可以是任意类型,包括数组和集合。当一个类包含了数组和集合成员时,索引器将大大简化对数组或集合成员的存取操作

  • 使用托管C++粘合C#和C++代码(一)

    C#在xml读写,数据库操纵,界面构造等很多方面性能卓越;C++的效率高,是底层开发的必备武器

  • C#开发中的反射机制

    反射的定义:审查元数据并收集关于它的类型信息的能力。元数据(编译以后的最基本数据单元)就是一大堆的表,当编译程序集或者模块时,编译器会创建一个类定义表,一个字段定义表,和一个方法定义表等

  • 深入C# 序列化(Serialize)、反序列化(Deserialize)

    C#中的序列化和反序列化,序列化是.NET运行时环境用来支持用户定义类型的流化的机制

  • Async和Await使异步编程更简单

    C#5.0中async和await两个关键字,这两个关键字简化了异步编程,之所以简化了,还是因为编译器给我们做了更多的工作