内存泄漏

来源:互联网 发布:奥飞数据 招股说明书 编辑:程序博客网 时间:2024/04/28 05:39

昨天使用STL(仅指gcc的SGI   STL,VC,borlandC的不同)时,用valgrind测试,总是发现有"not-freed   blocks",   于是我满脸迷惑的得出结论:使用SGI   STL会造成内存泄漏.  
   
  现在才知道使用了多年C/C++的我竟然并不清楚什么叫"内存泄漏".下面是我的一些心得,欢迎大家批评指正.  
   
  1.   "只要分配了内存没有释放,就会导致内存泄漏"   --   这是我以前的理解,   是片面的.  
  分配了的内存,如果它的指针没有丢失,就不算是泄漏.   一般说来,为static指针变量或全局的指针变量(它们的生存期是全局的)进行内存分配,如果没有释放它,虽然这也是"not-freed   blocks",但是它是"reachable"的.现代的OS会得到这些指针并去释放它.  
  下面是两个例子:  
   
  1)   使用全局指针或静态指针:  
  int   main()  
  {  
          static   int   *pi;     //   use   static   pointer:   not   a   real   memory   leak!!!  
          pi   =   new   int;  
          return   0;  
  }  
   
  使用valgrind检查:    
  valgrind   --leak-check=yes   --show-reachable=yes   -q   ./memtest  
   
  ==13764==   searching   for   pointers   to   1   not-freed   blocks.  
  ==13764==   checked   4488448   bytes.  
  ==13764==  
  ==13764==   4   bytes   in   1   blocks   are   still   reachable   in   loss   record   1   of   1  
  ==13764==         at   0xB74D27AB:   __builtin_new   (vg_replace_malloc.c:172)  
  ==13764==         by   0xB74D2802:   operator   new(unsigned)   (vg_replace_malloc.c:185)  
  ==13764==         by   0x8048491:   main   (in   /home/prog/valgrind/memtest)  
  ==13764==         by   0xB71C4BA6:   __libc_start_main   (in   /lib/libc-2.3.2.so)  
  ==13764==  
  ==13764==   LEAK   SUMMARY:  
  ==13764==         definitely   lost:   0   bytes   in   0   blocks.  
  ==13764==         possibly   lost:       0   bytes   in   0   blocks.  
  ==13764==         still   reachable:   4   bytes   in   1   blocks.  
  ==13764==                   suppressed:   0   bytes   in   0   blocks.  
  ==13764==  
  报告说有一个"not-freed   block",但这个block是reachable的,所以它不算是真正的memory   leak.  
   
  2)   如果使用的是局部指针:  
  int   main()  
  {  
          int   *pi;     //   use   local   pointer:   real   memory   leak!!!  
          pi   =   new   int[100];  
          return   0;  
  }  
   
   
  ==13782==   searching   for   pointers   to   1   not-freed   blocks.  
  ==13782==   checked   4484352   bytes.  
  ==13782==  
  ==13782==   4   bytes   in   1   blocks   are   definitely   lost   in   loss   record   1   of   1  
  ==13782==         at   0xB74D27AB:   __builtin_new   (vg_replace_malloc.c:172)  
  ==13782==         by   0xB74D2802:   operator   new(unsigned)   (vg_replace_malloc.c:185)  
  ==13782==         by   0x8048491:   main   (in   /home/prog/valgrind/memtest)  
  ==13782==         by   0xB71C4BA6:   __libc_start_main   (in   /lib/libc-2.3.2.so)  
  ==13782==  
  ==13782==   LEAK   SUMMARY:  
  ==13782==         definitely   lost:   4   bytes   in   1   blocks.  
  ==13782==         possibly   lost:       0   bytes   in   0   blocks.  
  ==13782==         still   reachable:   0   bytes   in   0   blocks.  
  ==13782==                   suppressed:   0   bytes   in   0   blocks.  
  ==13782==  
   
  报告说是"definitely   lost",这才是真正的memory   leak!  
   
  2.   当然,我们一般情况下是不会分配内存而不释放的.   它的典型应用便是著名的memory   pool   (我先前居然并不了解,寒~   幸好现在知道了).   其要领是为了提高内存管理的效率,针对特殊需要(如要分配的内存是等大的并且很小)去重载new,delete操作符.   它会避免每次申请很小的内存,而是一下子得到很大的一块,再自己去定义和使用.(出于效率的考虑,它最后并不释放)  
   
  我花了一下午时间分析的我用的SGI   STL的内存分配源码(我是在LINUX下使用g++的),  
  下面一段是在/usr/include/c++/3.2.3/bits/stl_alloc.h中找到的STL源码:  
      template<bool   __threads,   int   __inst>  
          class   __default_alloc_template  
          {  
          private:  
              enum   {_ALIGN   =   8};  
              enum   {_MAX_BYTES   =   128};  
              enum   {_NFREELISTS   =   _MAX_BYTES   /   _ALIGN};  
   
              union   _Obj  
              {  
                  union   _Obj*   _M_free_list_link;  
                  char                 _M_client_data[1];         //   The   client   sees   this.  
              };  
   
              static   _Obj*   volatile                   _S_free_list[_NFREELISTS];  
   
              //   Chunk   allocation   state.  
              static   char*                                     _S_start_free;  
              static   char*                                     _S_end_free;  
              static   size_t                                   _S_heap_size;  
   
              static   _STL_mutex_lock                 _S_node_allocator_lock;  
    ....  
   
  可以看到,在关键的数据结构上,它使用了静态的指针,并且定义了一些常量来控制内存块的大小等.  
   
  3.   对于这种技术是不是"隐式的内存泄漏"   ,   我认为不是.  
  隐式的内存泄漏指分配内存后,虽然也释放,但没有及时释放,导致内存消耗无法及时回收.在服务器编程时特别忌讳.  
  一般说来,   内存池的大小是可控,   不会一直增长下去.   从应用的层面上来说,它也是"释放"了内存了的.  

 

 

Valgrind是一款非常强大的工具集合,它包含有包括内存检测、CPU监测等多种工具,其中最常用的是内存检测功能,它能监测出以下的各种内存错误:
1. 访问非法内存区域
2. 使用未被初始化的内存区域
3. 非法释放内存,比如多次free一个内存
4. 内存泄露

使用步骤:
1. 使用valgrind前需要使用-g参数编译源程序以便生成debug信息
2. 在运行程序的命令行前加上valgrind,例如:valgrind myprog argvlist
   为了能够给出内存检测报告,需要加上--leak-check参数,那么上述命令就变成:valgrind --leak-check=yes myprog argvlist
   这个时候程序运行会非常慢,消耗资源也会大幅增加,这是正常的,不必担心。因为valgrind需要收集内存错误和泄露的详细信息。这被有些人认为是valgrind的缺点,但是对于一个大项目来说,这一点时间的消耗来监测程序的稳定性是值得的。
3. 阅读valgrind给出的报告
valgrind的其它参数:
--num-callers=N:指定报告中调用栈的层数,这在定位和跟踪错误的时候会比较有用
-v :可以打印出更加详细的信息
valgrind的报告:
"Invalid write/read...":这一般是访问非法的内存区域,比如数组越界等
"Conditional jump or move depends on uninitialised value(s)":使用了未初始化的变量(包括未初始化的局部变量和未初始化的堆区域)
"Mismatched free() / delete / delete []":这一般是非法释法内存的错误,比如多次free一个内存
"LEAK SUMMARY":这表示下面是内存泄露的信息(造成内存丢失的程序行可以通过查看这一行前面的清单来定位)
"definitely lost":肯定丢失的部分,这种报告必须处理
"possibly lost":可能丢失的部分,这是由于C/C++语言指针处理的特点造成的,这部分可能不太准确

这里只有一个简单的介绍,更多详细信息参见valgrind的官方网站:http://valgrind.org/docs/manual/



Valgrind 是在linux系统下开发应用程序时用于调试内存问题的工具。它尤其擅长发现内存管理的问题,它可以检查程序运行时的内存泄漏问题。

   它的官方网址是 http://www.valgrind.org/

   下载最新版本的Valgrind,目前是3.2.0。 wget http://www.valgrind.org/downloads/valkyrie-1.2.0.tar.bz2

   执行常规的安装步骤:./confgure && make && make install。注意: 系统必须安装QT的开发包。即便这样在make 时还是出现qplatformdefs.h这个文件找不到的情况,导致make失败。查找系统中的qplatformdefs.h 之后,发现没有存在于qt的标准头文件目录/usr/lib/qt-3.3/include。如是将/usr/lib/qt- 3.3/mkspecs/linux-g++/ 目录下该头文件复制标准头文件目录,重新make ,后面一切OK。

初次使用
编译如下代码:  gcc -Wall example.c -g -o example

#include <stdlib.h>

void f(void)
{
int* x = malloc(10 * sizeof(int));
x[10] = 0; // problem 1: heap block overrun
} // problem 2: memory leak -- x not freed

int main(void)
{
f();
return 0;
}

     注意:gcc 的-g 选项让Valgrind调试输出时指出相应信息的代码所在的行号。

valgrind --tool=memcheck --leak-check=yes ./example

==6742== Memcheck, a memory error detector for x86-linux.
==6742== Copyright (C) 2002-2004, and GNU GPL'd, by Julian Seward et al.
==6742== Using valgrind-2.2.0, a program supervision framework for x86-linux.
==6742== Copyright (C) 2000-2004, and GNU GPL'd, by Julian Seward et al.
==6742== For more details, rerun with: -v
==6742==
==6742== Invalid write of size 4
==6742==    at 0x8048384: f (example.c:6)
==6742==    by 0x80483AC: main (example.c:12)
==6742== Address 0x1B908050 is 0 bytes after a block of size 40 alloc'd
==6742==    at 0x1B904984: malloc (vg_replace_malloc.c:131)
==6742==    by 0x8048377: f (example.c:5)
==6742==    by 0x80483AC: main (example.c:12)
==6742==
==6742== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 12 from 1)
==6742== malloc/free: in use at exit: 40 bytes in 1 blocks.
==6742== malloc/free: 1 allocs, 0 frees, 40 bytes allocated.
==6742== For counts of detected errors, rerun with: -v
==6742== searching for pointers to 1 not-freed blocks.
==6742== checked 1360800 bytes.
==6742==
==6742==
==6742== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
==6742==    at 0x1B904984: malloc (vg_replace_malloc.c:131)
==6742==    by 0x8048377: f (example.c:5)
==6742==    by 0x80483AC: main (example.c:12)
==6742==
==6742== LEAK SUMMARY:
==6742==    definitely lost: 40 bytes in 1 blocks.
==6742==    possibly lost:   0 bytes in 0 blocks.
==6742==    still reachable: 0 bytes in 0 blocks.
==6742==         suppressed: 0 bytes in 0 blocks.
==6742== Reachable blocks (those to which a pointer was found) are not shown.
==6742== To see them, rerun with: --show-reachable=yes

   上面的C程序存在两个错误:1. 数组下标越界;2. 分配的内存没有释放,存在内存泄露的问题。对于错误1,看Valgrind的调试信息片断 ==6742== Invalid write of size 4
==6742==    at 0x8048384: f (example.c:6)
==6742==    by 0x80483AC: main (example.c:12)
==6742== Address 0x1B908050 is 0 bytes after a block of size 40 alloc'd
==6742==    at 0x1B904984: malloc (vg_replace_malloc.c:131)
==6742==    by 0x8048377: f (example.c:5)

对于错误2,看这个

==6742== malloc/free: 1 allocs, 0 frees, 40 bytes allocated.

......

==6742== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
==6742==    at 0x1B904984: malloc (vg_replace_malloc.c:131)
==6742==    by 0x8048377: f (example.c:5)
==6742==    by 0x80483AC: main (example.c:12)

 

 

要了解其详细使用方法,看下面的链接

http://www.ibm.com/developerworks/cn/linux/l-cn-valgrind/

原创粉丝点击