精华内容
下载资源
问答
  • 所谓的内存泄漏就是用动态变量机制new了一段内存空间,以后不再需要这个变量的时候没有delete它,或者在没有delete之前,指针又指向了另外一个地址,这样先前的空间就丢失了。内存泄漏最典型的情况就是在函数中申请...

    前言

    所谓的内存泄漏就是用动态变量机制new了一段内存空间,以后不再需要这个变量的时候没有delete它,或者在没有delete之前,指针又指向了另外一个地址,这样先前的空间就丢失了。内存泄漏最典型的情况就是在函数中申请了一段动态空间,在函数返回前就没有delete它,造成内存泄漏。

        此外对new操作的失败检查也是一个良好的编程习惯,虽然一般很少遇到,但是这种内存分配的失败不是不会发生的,用assert宏可以很轻松的完成这个任务了。  

      int *p = new int;
      assert(p != NULL);


     

     

     内存溢出(out of memory)
    是指程序在申请内存时,没有足够的内存空间供其使用。

      内存泄漏(memory leak)
    是指程序在申请内存后,无法释放已申请的内存空间,占用有用内存。

     注:内存泄漏最终会导致内存溢出
    简单理解,内存溢出就是要求分配的内存超出了系统所给的。内存泄漏是指向系统申请分配内存进行使用(new),但是用完后不归还(delete),导致占用有效内存。

    内存泄漏可分为4类:

    1.常发性内存泄漏 

     引起内存泄漏的代码会被很多次执行,每次执行的时候都会导致内存泄漏

    2.偶发性内存泄漏

    在某些特定的环境下执行引起内存泄漏的代码,才会引起内存泄漏

    从以上两种内存泄漏的方式来看,测试环境和测试方法在程序生命周期的重要性是不可或缺的。

    3.一次性内存泄漏

    代码只会执行一次,但总有一块内存发生泄漏,多见于构造类的时候,析构函数没有释放内存。

    4.隐式泄漏

    程序运行过程中不断的分配内存,直到结束时才释放内存,但一般服务器程序会运行较长的时间,不及时释放也会导致内存耗尽以至于内存泄漏。

    综上所述,一次性内存泄漏对用户的程序维护是没有什么实质性的伤害,但在实际生活中,我们还是尽可能要避免此类的事件发生。

    内存越界
    是指向系统申请一块内存后,使用时却超出申请范围。比如一些操作内存的函数:sprintf、strcpy、strcat、vsprintf、memcpy、memset、memmove。当造成内存泄漏的代码运行时,所带来的错误是无法避免的,通常会造成
    1.破坏了堆中内存内存分配信息数据
    2.破坏了程序其他对象的内存空间
    3.破坏了空闲内存块
    附:如果在之前你的程序运行一切正常,但因为你新增了几个类的成员变量或者修改了一部分代码(前提是保证你的这些修改是完全正确的)而导致程序发生错误,则因考虑是否是内存被破坏的原因了,重点排查内存是否越界。
    缓冲区溢出(栈溢出)
    程序为了临时存取数据的需要,一般会分配一些内存空间称为缓冲区。如果向缓冲区中写入缓冲区无法容纳的数据,机会造成缓冲区以外的存储单元被改写,称为缓冲区溢出。而栈溢出是缓冲区溢出的一种,原理也是相同的。分为上溢出和下溢出。其中,上溢出是指栈满而又向其增加新的数据,导致数据溢出;下溢出是指空栈而又进行删除操作等,导致空间溢出。

    如何检查和分析内存泄漏?

    因为内存泄漏是在堆内存中,所以对我们来说并不是可见的。通常我们可以借助MAT、LeakCanary等工具来检测应用程序是否存在内存泄漏。
    1、MAT是一款强大的内存分析工具,功能繁多而复杂。
    2、LeakCanary则是由Square开源的一款轻量级的第三方内存泄漏检测工具,当检测到程序中产生内存泄漏时,它将以最直观的方式告诉我们哪里产生了内存泄漏和导致谁泄漏了而不能被回收。

    展开全文
  • valgrind通常用来成分析程序性能及程序中的内存泄露错误 一Valgrind工具集简绍 Valgrind包含下列工具: 1、memcheck:检查程序中的内存问题,如泄漏、越界、非法指针等。 2、callgrind:检测程序代码的运行...

    valgrind通常用来成分析程序性能及程序中的内存泄露错误

     

    一 Valgrind工具集简绍

    Valgrind包含下列工具:

        1、memcheck:检查程序中的内存问题,如泄漏、越界、非法指针等。

        2、callgrind:检测程序代码的运行时间和调用过程,以及分析程序性能。

        3、cachegrind:分析CPU的cache命中率、丢失率,用于进行代码优化。

        4、helgrind:用于检查多线程程序的竞态条件。

        5、massif:堆栈分析器,指示程序中使用了多少堆内存等信息。

        6、lackey:

        7、nulgrind:

    这几个工具的使用是通过命令:valgrand --tool=name 程序名来分别调用的,当不指定tool参数时默认是 --tool=memcheck

     

    二 Valgrind工具详解

    1.Memcheck

        最常用的工具,用来检测程序中出现的内存问题,所有对内存的读写都会被检测到,一切对malloc、free、new、delete的调用都会被捕获。所以,它能检测以下问题:

           1、对未初始化内存的使用;

           2、读/写释放后的内存块;

           3、读/写超出malloc分配的内存块;

           4、读/写不适当的栈中内存块;

           5、内存泄漏,指向一块内存的指针永远丢失;

           6、不正确的malloc/free或new/delete匹配;

           7、memcpy()相关函数中的dst和src指针重叠。

    这些问题往往是C/C++程序员最头疼的问题,Memcheck能在这里帮上大忙。
    例如:

     

    [plain]  view plain copy

    1. #include <stdlib.h>  
    2. #include <malloc.h>  
    3. #include <string.h>  
    4.   
    5. void test()  
    6. {  
    7.     int *ptr = malloc(sizeof(int)*10);  
    8.   
    9.     ptr[10] = 7; // 内存越界  
    10.   
    11.     memcpy(ptr +1, ptr, 5); // 踩内存  
    12.   
    13.   
    14.     free(ptr);   
    15.     free(ptr);// 重复释放  
    16.   
    17.     int *p1;  
    18.     *p1 = 1; // 非法指针  
    19. }  
    20.   
    21. int main(void)  
    22. {  
    23.     test();  
    24.     return 0;  
    25. }  

    将程序编译生成可执行文件后执行: valgrind --leak-check=full ./程序名

     

    输出结果如下:

    ==4832== Memcheck, a memory error detector
    ==4832== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
    ==4832== Using Valgrind-3.6.1 and LibVEX; rerun with -h for copyright info
    ==4832== Command: ./tmp
    ==4832== 
    ==4832== Invalid write of size 4      // 内存越界
    ==4832==    at 0x804843F: test (in /home/yanghao/Desktop/testC/testmem/tmp)
    ==4832==    by 0x804848D: main (in /home/yanghao/Desktop/testC/testmem/tmp)
    ==4832==  Address 0x41a6050 is 0 bytes after a block of size 40 alloc'd
    ==4832==    at 0x4026864: malloc (vg_replace_malloc.c:236)
    ==4832==    by 0x8048435: test (in /home/yanghao/Desktop/testC/testmem/tmp)
    ==4832==    by 0x804848D: main (in /home/yanghao/Desktop/testC/testmem/tmp)
    ==4832== 
    ==4832== Source and destination overlap in memcpy(0x41a602c, 0x41a6028, 5) // 踩内存
    ==4832==    at 0x4027BD6: memcpy (mc_replace_strmem.c:635)
    ==4832==    by 0x8048461: test (in /home/yanghao/Desktop/testC/testmem/tmp)
    ==4832==    by 0x804848D: main (in /home/yanghao/Desktop/testC/testmem/tmp)
    ==4832== 
    ==4832== Invalid free() / delete / delete[] // 重复释放
    ==4832==    at 0x4025BF0: free (vg_replace_malloc.c:366)
    ==4832==    by 0x8048477: test (in /home/yanghao/Desktop/testC/testmem/tmp)
    ==4832==    by 0x804848D: main (in /home/yanghao/Desktop/testC/testmem/tmp)
    ==4832==  Address 0x41a6028 is 0 bytes inside a block of size 40 free'd
    ==4832==    at 0x4025BF0: free (vg_replace_malloc.c:366)
    ==4832==    by 0x804846C: test (in /home/yanghao/Desktop/testC/testmem/tmp)
    ==4832==    by 0x804848D: main (in /home/yanghao/Desktop/testC/testmem/tmp)
    ==4832== 
    ==4832== Use of uninitialised value of size 4 // 非法指针
    ==4832==    at 0x804847B: test (in /home/yanghao/Desktop/testC/testmem/tmp)
    ==4832==    by 0x804848D: main (in /home/yanghao/Desktop/testC/testmem/tmp)
    ==4832== 
    ==4832== 
    ==4832== Process terminating with default action of signal 11 (SIGSEGV) //由于非法指针赋值导致的程序崩溃
    ==4832==  Bad permissions for mapped region at address 0x419FFF4
    ==4832==    at 0x804847B: test (in /home/yanghao/Desktop/testC/testmem/tmp)
    ==4832==    by 0x804848D: main (in /home/yanghao/Desktop/testC/testmem/tmp)
    ==4832== 
    ==4832== HEAP SUMMARY:
    ==4832==     in use at exit: 0 bytes in 0 blocks
    ==4832==   total heap usage: 1 allocs, 2 frees, 40 bytes allocated
    ==4832== 
    ==4832== All heap blocks were freed -- no leaks are possible
    ==4832== 
    ==4832== For counts of detected and suppressed errors, rerun with: -v
    ==4832== Use --track-origins=yes to see where uninitialised values come from
    ==4832== ERROR SUMMARY: 4 errors from 4 contexts (suppressed: 11 from 6)
    Segmentation fault

    从valgrind的检测输出结果看,这几个错误都找了出来。

     

    2.Callgrind

        和gprof类似的分析工具,但它对程序的运行观察更是入微,能给我们提供更多的信息。和gprof不同,它不需要在编译源代码时附加特殊选项,但加上调试选项是推荐的。Callgrind收集程序运行时的一些数据,建立函数调用关系图,还可以有选择地进行cache模拟。在运行结束时,它会把分析数据写入一个文件。callgrind_annotate可以把这个文件的内容转化成可读的形式。

    生成可视化的图形需要下载gprof2dot:http://jrfonseca.googlecode.com/svn/trunk/gprof2dot/gprof2dot.py

    这是个Python脚本,把它下载之后修改其权限chmod +7 gprof2dot.py ,并把这个脚本添加到$PATH路径中的任一文件夹下,我是将它放到了/usr/bin目录下,这样就可以直接在终端下执行gprof2dot.py了。

       Callgrind可以生成程序性能分析的图形,首先来说说程序性能分析的工具吧,通常可以使用gnu自带的gprof,它的使用方法是:在编译程序时添加-pg参数,例如:

     

    [plain]  view plain copy

    1. #include <stdio.h>  
    2. #include <malloc.h>  
    3. void test()  
    4. {  
    5.     sleep(1);  
    6. }  
    7. void f()  
    8. {  
    9.     int i;  
    10.     for( i = 0; i < 5; i ++)  
    11.         test();  
    12. }  
    13. int main()  
    14. {  
    15.     f();  
    16.     printf("process is over!\n");  
    17.     return 0;  
    18. }  

    首先执行 gcc -pg -o tmp tmp.c,然后运行该程序./tmp,程序运行完成后会在当前目录下生成gmon.out文件(这个文件gprof在分析程序时需要),
    再执行gprof ./tmp | gprof2dot.py |dot -Tpng -o report.png,打开 report.png结果:

     

    显示test被调用了5次,程序中耗时所占百分比最多的是test函数。

    再来看 Callgrind的生成调用图过程吧,执行:valgrind --tool=callgrind ./tmp,执行完成后在目录下生成"callgrind.out.XXX"的文件这是分析文件,可以直接利用:callgrind_annotate callgrind.out.XXX 打印结果,也可以使用:gprof2dot.py -f callgrind callgrind.out.XXX |dot -Tpng -o report.png 来生成图形化结果:

    它生成的结果非常详细,甚至连函数入口,及库函数调用都标识出来了。

     

    3.Cachegrind

           Cache分析器,它模拟CPU中的一级缓存I1,Dl和二级缓存,能够精确地指出程序中cache的丢失和命中。如果需要,它还能够为我们提供cache丢失次数,内存引用次数,以及每行代码,每个函数,每个模块,整个程序产生的指令数。这对优化程序有很大的帮助。

        作一下广告:valgrind自身利用该工具在过去几个月内使性能提高了25%-30%。据早先报道,kde的开发team也对valgrind在提高kde性能方面的帮助表示感谢。

    它的使用方法也是:valgrind --tool=cachegrind 程序名,

    4.Helgrind

        它主要用来检查多线程程序中出现的竞争问题。Helgrind寻找内存中被多个线程访问,而又没有一贯加锁的区域,这些区域往往是线程之间失去同步的地方,而且会导致难以发掘的错误。Helgrind实现了名为“Eraser”的竞争检测算法,并做了进一步改进,减少了报告错误的次数。不过,Helgrind仍然处于实验阶段。

    首先举一个竞态的例子吧:

    [plain]  view plain copy

    1. #include <stdio.h>  
    2. #include <pthread.h>  
    3. #define NLOOP 50  
    4. int counter = 0; /* incremented by threads */  
    5. void *threadfn(void *);  
    6.   
    7. int main(int argc, char **argv)  
    8. {  
    9.     pthread_t tid1, tid2,tid3;  
    10.   
    11.   
    12.     pthread_create(&tid1, NULL, &threadfn, NULL);  
    13.     pthread_create(&tid2, NULL, &threadfn, NULL);  
    14.     pthread_create(&tid3, NULL, &threadfn, NULL);  
    15.   
    16.   
    17.     /* wait for both threads to terminate */  
    18.     pthread_join(tid1, NULL);  
    19.     pthread_join(tid2, NULL);  
    20.     pthread_join(tid3, NULL);  
    21.   
    22.   
    23.     return 0;  
    24. }  
    25.   
    26. void *threadfn(void *vptr)  
    27. {  
    28.       int i, val;  
    29.       for (i = 0; i < NLOOP; i++) {  
    30.     val = counter;  
    31.     printf("%x: %d \n", (unsigned int)pthread_self(),  val+1);  
    32.     counter = val+1;  
    33.       }  
    34.       return NULL;  
    35. }  

     

    这段程序的 竞态在30~32行,我们想要的效果是3个线程分别对全局变量累加50次,最后全局变量的值为150,由于这里没有加锁,很明显竞态使得程序不能达到我们的目标。我们来看Helgrind是如何帮我们检测到竞态的。 先编译程序:gcc -o test thread.c -lpthread ,然后执行:valgrind --tool=helgrind ./ test 输出结果如下:

    49c0b70: 1 
    49c0b70: 2 
    ==4666== Thread #3 was created
    ==4666==    at 0x412E9D8: clone (clone.S:111)
    ==4666==    by 0x40494B5: pthread_create@@GLIBC_2.1 (createthread.c:256)
    ==4666==    by 0x4026E2D: pthread_create_WRK (hg_intercepts.c:257)
    ==4666==    by 0x4026F8B: pthread_create@* (hg_intercepts.c:288)
    ==4666==    by 0x8048524: main (in /home/yanghao/Desktop/testC/testmem/a.out)
    ==4666== 
    ==4666== Thread #2 was created
    ==4666==    at 0x412E9D8: clone (clone.S:111)
    ==4666==    by 0x40494B5: pthread_create@@GLIBC_2.1 (createthread.c:256)
    ==4666==    by 0x4026E2D: pthread_create_WRK (hg_intercepts.c:257)
    ==4666==    by 0x4026F8B: pthread_create@* (hg_intercepts.c:288)
    ==4666==    by 0x8048500: main (in /home/yanghao/Desktop/testC/testmem/a.out)
    ==4666== 
    ==4666== Possible data race during read of size 4 at 0x804a028 by thread #3
    ==4666==    at 0x804859C: threadfn (in /home/yanghao/Desktop/testC/testmem/a.out)
    ==4666==    by 0x4026F60: mythread_wrapper (hg_intercepts.c:221)
    ==4666==    by 0x4048E98: start_thread (pthread_create.c:304)
    ==4666==    by 0x412E9ED: clone (clone.S:130)
    ==4666==  This conflicts with a previous write of size 4 by thread #2
    ==4666==    at 0x80485CA: threadfn (in /home/yanghao/Desktop/testC/testmem/a.out)
    ==4666==    by 0x4026F60: mythread_wrapper (hg_intercepts.c:221)
    ==4666==    by 0x4048E98: start_thread (pthread_create.c:304)
    ==4666==    by 0x412E9ED: clone (clone.S:130)
    ==4666== 
    ==4666== Possible data race during write of size 4 at 0x804a028 by thread #2
    ==4666==    at 0x80485CA: threadfn (in /home/yanghao/Desktop/testC/testmem/a.out)
    ==4666==    by 0x4026F60: mythread_wrapper (hg_intercepts.c:221)
    ==4666==    by 0x4048E98: start_thread (pthread_create.c:304)
    ==4666==    by 0x412E9ED: clone (clone.S:130)
    ==4666==  This conflicts with a previous read of size 4 by thread #3
    ==4666==    at 0x804859C: threadfn (in /home/yanghao/Desktop/testC/testmem/a.out)
    ==4666==    by 0x4026F60: mythread_wrapper (hg_intercepts.c:221)
    ==4666==    by 0x4048E98: start_thread (pthread_create.c:304)
    ==4666==    by 0x412E9ED: clone (clone.S:130)
    ==4666== 
    49c0b70: 3 
    ......
    55c1b70: 51 
    ==4666== 
    ==4666== For counts of detected and suppressed errors, rerun with: -v
    ==4666== Use --history-level=approx or =none to gain increased speed, at
    ==4666== the cost of reduced accuracy of conflicting-access information
    ==4666== ERROR SUMMARY: 8 errors from 2 contexts (suppressed: 99 from 31)

    helgrind成功的找到了 竞态的所在位置,标红所示。

     

    5. Massif

        堆栈分析器,它能测量程序在堆栈中使用了多少内存,告诉我们堆块,堆管理块和栈的大小。Massif能帮助我们减少内存的使用,在带有虚拟内存的现代系统中,它还能够加速我们程序的运行,减少程序停留在交换区中的几率。

           Massif对内存的分配和释放做profile。程序开发者通过它可以深入了解程序的内存使用行为,从而对内存使用进行优化。这个功能对C++尤其有用,因为C++有很多隐藏的内存分配和释放。

     

    此外,lackey和nulgrind也会提供。Lackey是小型工具,很少用到;Nulgrind只是为开发者展示如何创建一个工具。我们就不做介绍了。

    三 使用Valgrind

           Valgrind使用起来非常简单,你甚至不需要重新编译你的程序就可以用它。当然如果要达到最好的效果,获得最准确的信息,还是需要按要求重新编译一下的。比如在使用memcheck的时候,最好关闭优化选项。

           valgrind命令的格式如下:

           valgrind [valgrind-options] your-prog [your-prog options]

    valgrind --tool=massif --stacks=yes ./test

    (这个工具有个bug, 只有程序中出现new或者malloc之类的堆操作,才会统计栈的使用,否则只统计堆的使用)

    一些常用的选项如下:

    选项

    作用

    -h --help

    显示帮助信息。

    --version

    显示valgrind内核的版本,每个工具都有各自的版本。

    -q --quiet

    安静地运行,只打印错误信息。

    -v --verbose

    打印更详细的信息。

    --tool=<toolname> [default: memcheck]

    最常用的选项。运行valgrind中名为toolname的工具。如果省略工具名,默认运行memcheck。

    --db-attach=<yes|no> [default: no]

    绑定到调试器上,便于调试错误。

    展开全文
  • 内存泄露

    2014-02-23 15:54:25
    主要有四类:数组越界指针悬空,动态内存申请(mallocfree),文件的打开(fopenfclose) 存在内存错误的 C C++ 程序会导致各种问题。如果它们泄漏内存,则运行速度会逐渐变慢,并最终停止运行;...

    转http://blog.csdn.net/wjflyhigh/article/details/10415359

    主要有四类:数组越界,指针悬空,动态内存申请(malloc和free),文件的打开(fopen和fclose)


    存在内存错误的 C 和 C++ 程序会导致各种问题。如果它们泄漏内存,则运行速度会逐渐变慢,并最终停止运行;如果覆盖内存,则会变得非常脆弱,很容易受到恶意用户的攻击。从 1988 年著名的莫里斯蠕虫 攻击到有关 Flash Player 和其他关键的零售级程序的最新安全警报都与缓冲区溢出有关:“大多数计算机安全漏洞都是缓冲区溢出”,Rodney Bates 在 2004 年写道。

    在可以使用 C 或 C++ 的地方,也广泛支持使用其他许多通用语言(如 Java?、Ruby、Haskell、C#、Perl、Smalltalk 等),每种语言都有众多的爱好者和各自的优点。但是,从计算角度来看,每种编程语言优于 C 或 C++ 的主要优点都与便于内存管理密切相关。与内存相关的编程是如此重要,而在实践中正确应用又是如此困难,以致于它支配着面向对象编程语言、功能性编程语言、高级编程语言、声明性编程语言和另外一些编程语言的所有其他变量或理论。

    与少数其他类型的常见错误一样,内存错误还是一种隐性危害:它们很难再现,症状通常不能在相应的源代码中找到。例如,无论何时何地发生内存泄漏,都可能表现为应用程序完全无法接受,同时内存泄漏不是显而易见。

    因此,出于所有这些原因,需要特别关注 C 和 C++ 编程的内存问题。让我们看一看如何解决这些问题,先不谈是哪种语言。

    内存错误的类别

    首先,不要失去信心。有很多办法可以对付内存问题。我们先列出所有可能存在的实际问题:

    内存泄漏 错误分配,包括大量增加 free() 释放的内存和未初始化的引用 悬空指针 数组边界违规 这是所有类型。即使迁移到 C++ 面向对象的语言,这些类型也不会有明显变化;无论数据是简单类型还是 C 语言的 struct 或 C++ 的类,C 和 C++ 中内存管理和引用的模型在原理上都是相同的。以下内容绝大部分是“纯 C”语言,对于扩展到 C++ 主要留作练习使用。

    内存泄漏

    在分配资源时会发生内存泄漏,但是它从不回收。下面是一个可能出错的模型(请参见清单 1):



    void f1(char *explanation) { char *p1; p1 = malloc(100); sprintf(p1,"The f1 error occurred because of '%s'.", explanation); local_log(p1); }

    您看到问题了吗?除非 local_log() 对 free() 释放内存具有不寻常的响应能力,否则每次对 f1 的调用都会泄漏 100 字节。在记忆棒增量分发数兆字节内存时,一次泄漏是微不足道的,但是连续操作数小时后,即使如此小的泄漏也会削弱应用程序。

    在实际的 C 和 C++ 编程中,这不足以影响您对 malloc() 或 new 的使用,本部分开头的句子提到了“资源”不是仅指“内存”,因为还有类似以下内容的示例(请参见清单 2)。FILE 句柄可能与内存块不同,但是必须对它们给予同等关注:



    int getkey(char *filename) { FILE *fp; int key; fp = fopen(filename, "r"); fscanf(fp, "%d", &key); return key; }

    fopen 的语义需要补充性的 fclose。在没有 fclose() 的情况下,C 标准不能指定发生的情况时,很可能是内存泄漏。其他资源(如信号量、网络句柄、数据库连接等)同样值得考虑。

    内存错误分配

    错误分配的管理不是很困难。下面是一个错误分配示例(请参见清单 3):



    void f2(int datum) { int *p2; /* Uh-oh! No one has initialized p2. */ *p2 = datum; ... }

    关于此类错误的好消息是,它们一般具有显著结果。在 AIX® 下,对未初始化指针的分配通常会立即导致 segmentation fault 错误。它的好处是任何此类错误都会被快速地检测到;与花费数月时间才能确定且难以再现的错误相比,检测此类错误的代价要小得多。

    在此错误类型中存在多个变种。free() 释放的内存比 malloc() 更频繁(请参见清单 4):



    /* Allocate once, free twice. */ void f3() { char *p, *pp; p = malloc(10);pp=p;free(p); ... free(pp); } /* Allocate zero times, free once. */ void f4() { char *p;.../* Note that p remains uninitialized here. */ free(p); }

    这些错误通常也不太严重。尽管 C 标准在这些情形中没有定义具体行为,但典型的实现将忽略错误,或者快速而明确地对它们进行标记;总之,这些都是安全情形。

    悬空指针

    悬空指针比较棘手。当程序员在内存资源释放后使用资源时会发生悬空指针(请参见清单 5):



    void f8() { struct x *xp; xp = (struct x *) malloc(sizeof (struct x)); xp.q = 13; ... free(xp); ... /* Problem! There's no guarantee that the memory block to which xp points hasn't been overwritten. */ return xp.q; }

    传统的“调试”难以隔离悬空指针。由于下面两个明显原因,它们很难再现:

    即使影响提前释放内存范围的代码已本地化,内存的使用仍然可能取决于应用程序甚至(在极端情况下)不同进程中的其他执行位置。 悬空指针可能发生在以微妙方式使用内存的代码中。结果是,即使内存在释放后立即被覆盖,并且新指向的值不同于预期值,也很难识别出新值是错误值。 悬空指针不断威胁着 C 或 C++ 程序的运行状态。

    数组边界违规

    数组边界违规十分危险,它是内存错误管理的最后一个主要类别。回头看一下清单 1;如果 explanation 的长度超过 80,则会发生什么情况?回答:难以预料,但是它可能与良好情形相差甚远。特别是,C 复制一个字符串,该字符串不适于为它分配的 100 个字符。在任何常规实现中,“超过的”字符会覆盖内存中的其他数据。内存中数据分配的布局非常复杂并且难以再现,所以任何症状都不可能追溯到源代码级别的具体错误。这些错误通常会导致数百万美元的损失。

    /

    static char *important_pointer = NULL; void f9() { if (!important_pointer) important_pointer = malloc(IMPORTANT_SIZE); ... if (condition) /* Ooops! We just lost the reference important_pointer already held. */ important_pointer = malloc(DIFFERENT_SIZE); ... }do not返回局部指针变量或者局部变量的指针,除非是一个static局部变量char *f0()
    {
        char temp[]="123456789"; //加上static 才是正确的    return temp;
    }
    展开全文
  • 1. Valgrind简介 官网Valgrind介绍是一款用于内存调试、内存泄漏检测以及性能...1)memcheck:检查程序中的内存问题,如泄漏、越界、非法指针等(本文重点学习)。 2)callgrind:检测程序代码的运行时间调用过程,...

    1. Valgrind简介

    官网Valgrind介绍是一款用于内存调试、内存泄漏检测以及性能分析的软件开发工具。这些工具之中最有名的是Memcheck。它能够识别很多C或者C++程序中内存相关的错误,这些错误会导致程序崩溃或者出现不可预知的行为。

    1)memcheck:检查程序中的内存问题,如泄漏、越界、非法指针等(本文重点学习)

    2)callgrind:检测程序代码的运行时间和调用过程,以及分析程序性能。

    3)cachegrind:分析CPU的cache命中率、丢失率,用于进行代码优化。

    4)helgrind:用于检查多线程程序的竞态条件。

    5)massif:堆栈分析器,指示程序中使用了多少堆内存等信息。

    这几个工具的使用是通过命令:valgrand --tool=name 程序名来分别调用的,当不指定tool参数时默认是 --tool=memcheck

    2. Valgrind安装

    官网下载

    # wget http://valgrind.org/downloads/valgrind-3.15.0.tar.bz2

    # tar -xjvf valgrind-3.15.0.tar.bz2 

    # cd valgrind-3.15.0/

    # ./autogen.sh

           如果报错上图错误:需要安装autoconf和automake两个工具,请先查看“安装autoconf“和”安装automake“两个步骤

           解决办法: # yum  -y install install autoconf automake libtool

          安装成功以后,继续执行 # ./autogen.sh

    # ./configure

    # make

    # make install

    # valgrind --version    检查安装是否成功

     

    Valgrind命令的格式如下:

    valgrind [valgrind-options] your-prog [your-prog options]

    使用如下命令: valgrind  --tool=memcheck  --leak-check=full  --log-file=reportleak  ./程序名

             参数含意:

              --tool=memcheck 使用的工具,默认即为memcheck

              --leak-check=full  要求对与内存错误,给出全部信息

              --log-file=***  表示错误输出文件名

    3. Valgrind使用

    代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <malloc.h>  
    #include <string.h>
    
    int main()
    {
        int *p = new int[5];
        printf("address:%p\n", p);	//申请空间,未释放
    	
        int *ptr = new int[10];;  
        ptr[10] = 7; // 内存越界  
    	
        memcpy(ptr +1, ptr, 5); // 踩内存  
    	
        delete []ptr;   
        delete []ptr;// 重复释放  
      
        int *p1;  
        *p1 = 1; // 非法指针  
        return 0;
    }
    
    

    编译:# g++ -g -o main main.cpp

    valgrind被设计成非侵入式的,它直接工作于可执行文件上,可直接检查编译后的程序。

    查看内存信息:

    基本命令:

    #  valgrind --tool=memcheck --leak-check=yes ./main

    检测结果输入到文件valgrind_report.log中:

    # valgrind --tool=memcheck --leak-check=yes --show-reachable=yes --run-libc-freeres=yes --log-file=./valgrind_report.log ./main 

    结果打印到屏幕上:

    #  valgrind --tool=memcheck --leak-check=yes --show-reachable=yes --run-libc-freeres=yes ./main

    ==1301== Memcheck, a memory error detector
    ==1301== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
    ==1301== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
    ==1301== Command: ./main
    ==1301==
    ==1301== Invalid write of size 4	 // 内存越界
    ==1301==    at 0x400649: main (main.cpp:12)
    ==1301==  Address 0x5a230c8 is 0 bytes after a block of size 40 alloc'd
    ==1301==    at 0x4C2AB28: operator new[](unsigned long) (vg_replace_malloc.c:433)
    ==1301==    by 0x40063C: main (main.cpp:11)
    ==1301==
    ==1301== Source and destination overlap in memcpy(0x5a230a4, 0x5a230a0, 5)	// 踩内存
    ==1301==    at 0x4C2E70D: memcpy@@GLIBC_2.14 (vg_replace_strmem.c:1035)
    ==1301==    by 0x40066A: main (main.cpp:14)
    ==1301==
    ==1301== Invalid free() / delete / delete[] / realloc()	// 重复释放
    ==1301==    at 0x4C2BA7F: operator delete[](void*) (vg_replace_malloc.c:651)
    ==1301==    by 0x400690: main (main.cpp:17)
    ==1301==  Address 0x5a230a0 is 0 bytes inside a block of size 40 free'd
    ==1301==    at 0x4C2BA7F: operator delete[](void*) (vg_replace_malloc.c:651)
    ==1301==    by 0x40067D: main (main.cpp:16)
    ==1301==  Block was alloc'd at
    ==1301==    at 0x4C2AB28: operator new[](unsigned long) (vg_replace_malloc.c:433)
    ==1301==    by 0x40063C: main (main.cpp:11)
    ==1301==
    ==1301== Use of uninitialised value of size 8	 // 非法指针
    ==1301==    at 0x400695: main (main.cpp:20)
    ==1301==
    ==1301==
    ==1301== Process terminating with default action of signal 11 (SIGSEGV)	//由于非法指针赋值导致的程序崩溃
    ==1301==  Bad permissions for mapped region at address 0x400530
    ==1301==    at 0x400695: main (main.cpp:20)
    ==1301==
    ==1301== HEAP SUMMARY:
    ==1301==     in use at exit: 20 bytes in 1 blocks
    ==1301==   total heap usage: 2 allocs, 2 frees, 60 bytes allocated
    ==1301==
    ==1301== 20 bytes in 1 blocks are still reachable in loss record 1 of 1	//指针未释放
    ==1301==    at 0x4C2AB28: operator new[](unsigned long) (vg_replace_malloc.c:433)
    ==1301==    by 0x40062E: main (main.cpp:8)
    ==1301==
    ==1301== LEAK SUMMARY:
    ==1301==    definitely lost: 0 bytes in 0 blocks
    ==1301==    indirectly lost: 0 bytes in 0 blocks
    ==1301==      possibly lost: 0 bytes in 0 blocks
    ==1301==    still reachable: 20 bytes in 1 blocks
    ==1301==         suppressed: 0 bytes in 0 blocks
    ==1301==
    ==1301== Use --track-origins=yes to see where uninitialised values come from
    ==1301== For lists of detected and suppressed errors, rerun with: -s
    ==1301== ERROR SUMMARY: 4 errors from 4 contexts (suppressed: 0 from 0)
    段错误
    

    修改代码中存在内存检测有问题的地方,重新检测后发现:

     检查程序中的内存问题已经解决。

    注意:Valgrind不检查静态分配数组的使用情况!所以对静态分配的数组,Valgrind表示无能为力

    4. 结论分析

    Memcheck 工具可以检查以下的程序错误:

    (1)使用未初始化的内存 (Use of uninitialised memory)

    (2)使用已经释放了的内存 (Reading/writing memory after it has been free'd)

    (3)使用超过malloc分配的内存空间(Reading/writing off the end of malloc'd blocks)

    (4)对堆栈的非法访问 (Reading/writing inappropriate areas on the stack)

    (5)申请的空间是否有释放 (Memory leaks – where pointers to malloc'd blocks are lost forever)

    (6)malloc/free/new/delete申请和释放内存的匹配(Mismatched use of malloc/new/new [] vs free/delete/delete [])

    (7)src和dst的重叠(Overlapping src and dst pointers in memcpy() and related functions)

    (8)重复free(Invalid free() / delete / delete[] / realloc())

    如果条件允许,你也可以结合Coverity静态内存检查和Valgrind内存检测。注意:Coverity静态检测不能检测动态内存问题。

    5.  补充

    ASan(Linux):gcc4.8以上版本自带的内存检查工具

    特点:方便好用、比Valgrind性能影响小

    asan编译和链接的程序宕机的内存位置和变量名,文件行数,变量来源,线程信息等等非常全面的信息当做error输出,方便定位问题。

    总结一下,网上说asan在检测程序内存方面的功能实在是强大,比valgrind检查内存泄漏好点,因为valgrind对性能的影响实在是太大,完全不能放到真实环境测试,并且asan给出的问题报告相当详细,基本上看一次错误报告就能找到崩溃原因,有时候dump文件、core文件的信息由于优化和其他程序上下文的差异会导致提供的信息基本上没什么作用,这个时候你就需要它们来助你跳坑。

     

    展开全文
  • 动态内存越界访问06. 分配空间后没有释放07. 不匹配使用delete或者free08. 两次释放同一块内存09. 总结10. 附录 01. 前言 C/C++运行高效,不管是操作系统内核还是对性有要求的程序(比如游戏引擎)都要求使用C/...
  • valgrind通常用来成分析程序性能及程序中的内存泄露错误 一Valgrind工具集简绍 Valgrind包含下列工具: 1、memcheck:检查程序中的内存问题,如泄漏、越界、非法指针等。 2、callgrind:检测程序代码的运行时间...
  • Valgrind通常用来成分析程序性能及程序中的内存泄露错误   一 Valgrind工具集简绍 Valgrind包含下列工具:  1、memcheck:检查程序中的内存问题,如泄漏、越界、非法指针等。  2、callgrind:检测程序代码...
  • 1、号称功能: 内存泄露检测 (检测未释放内存,即 动态内存开辟未释放的情况) 2、检测多次调用free,free错误地址 3、检测内存访问的上越界和越界 4、检测对野指针进行的写操作 其他内存检测工具有mtrac.....
  • valgrind通常用来成分析程序性能及程序中的内存泄露错误一Valgrind工具集简绍Valgrind包含下列工具:1、memcheck:检查程序中的内存问题,如泄漏、越界、非法指针等。2、callgrind:检测程序代码的运行时间调用...
  • Valgrind检查内存泄露

    2018-02-27 17:09:43
    Valgrind通常用来成分析程序性能及程序中的内存泄露错误一 Valgrind工具集简绍Valgrind包含下列工具: 1、memcheck:检查程序中的内存问题,如泄漏、越界、非法指针等。 2、callgrind:检测程序代码的运行时间...
  • valgrind通常用来成分析程序性能及程序中的内存泄露错误 一Valgrind工具集简绍 Valgrind包含下列工具: 1、memcheck:检查程序中的内存问题,如泄漏、越界、非法指针等。 2、callgrind:检测程序代码的运...
  • valgrind通常用来成分析程序性能及程序中的内存泄露错误 一Valgrind工具集简绍 Valgrind包含下列工具: 1、memcheck:检查程序中的内存问题,如泄漏、越界、非法指针等。 2、callgrind:检测程序代码的运行时间...
  • valgrind通常用来成分析程序性能及程序中的内存泄露错误 Valgrind工具集简绍 Valgrind包含下列工具: 1 memcheck:检查程序中的内存问题,如泄漏、越界、非法指针等。 最常用的工具,用来检测程序中出现的内存...
  • JAVA虚拟机内存结构

    2018-01-23 09:54:11
    2、相对安全的内存管理和访问机制,避免了绝大多数的内存泄露和指针越界问题 c、c++等语言需要自己去为对象申请内存,同时主动维护其生命周期,而JAVA在JVM的自动内存管理机制下,不需要为每个new操作去写配对的...
  • valgrind通常用来成分析程序性能及程序中的内存泄露错误一 Valgrind工具集简绍Valgrind包含下列工具:1、memcheck:检查程序中的内存问题,如泄漏、越界、非法指针等。2、callgrind:检测程序代码的运行时间调用...

空空如也

空空如也

1 2 3 4 5 6
收藏数 107
精华内容 42
关键字:

内存泄露和指针越界