官方概念
Valgrind是一套Linux下,开放源代码(GPL V2)的仿真调试工具的集合。Valgrind由内核(core)以及基于内核的其他调试工具组成。内核类似于一个框架(framework),它模拟了一个CPU环境,并提供服务给其他工具;而其他工具则类似于插件 (plug-in),利用内核提供的服务完成各种特定的内存调试任务。
Valgrind 体系结构
Valgrind包括如下一些工具:
◼ Memcheck。这是valgrind应用最广泛的工具,一个重量级的内存检查器,能够
发现开发中绝大多数内存错误使用情况,比如:使用未初始化的内存,使用已
经释放了的内存,内存访问越界等。这也是本文将重点介绍的部分。
◼ Callgrind。它主要用来检查程序中函数调用过程中出现的问题。
◼ Cachegrind。它主要用来检查程序中缓存使用出现的问题。
◼ Helgrind。它主要用来检查多线程程序中出现的竞争问题。
◼ Massif。它主要用来检查程序中堆栈使用中出现的问题。
◼ Extension。可以利用core提供的功能,自己编写特定的内存调试工具。
写一个例子,内存泄露
sample.c
#include <stdio.h> #include <stdlib.h> void fun( ) { int *p = (int *)malloc(10*sizeof(int)); p[11] = 0; // 此处越界 free(p); } int main(void) { fun(); return 0; }
gcc -g -o sample sample.c -- 使用-g 加上调试
valgrind --tool=memcheck ./sample
valgrind 会提示 哪一行出了问题
1. 这是一个对内存的非法写操作,非法写操作的内存是4 bytes。
2. 发生错误时的函数堆栈,以及具体的源代码行号。
3. 非法写操作的具体地址空间。
在例如,把上面的例子,malloc的内存,不free
#include <stdio.h> #include <stdlib.h> void fun( ) { int *p = (int *)malloc(10*sizeof(int)); p[11] = 0; // 此处越界 //free(p); } int main(void) { fun(); return 0; }
1块内存被分配,0块内存被释放 -- 内存泄露
第2个demo,未初始化的变量
badloop.c
#include <stdio.h> int main(void) { int a[5]; int i, s;//s为局部变量,且为初始化 a[0] = a[1] = a[2] = a[3] = a[4] = 0; for (i = 0; i < 5; i++) { s += a[i]; //使用了未初始化的变量,结果未知 } if (s == 888) { printf("sum is %d\n", s); } return 0; }
gcc -g -o badloop badloop.c
valgrind --tool=memcheck ./badloop
动态内存管理错误
mem.c
#include <stdio.h> #include <stdlib.h> int main(void) { char * p = (char *)malloc(10); char *pt = p; for(int i = 0;i<10;i++) { p[i] = i; } delete p ; //使用c++ 的 delete 去释放p ,delete 是兼容c语言的 释放内存机制的 *pt = 'o'; free(pt); return 0; }
g++ -g -o mem mem.c
valgrind --tool=memcheck ./mem
常见的内存动态管理错误包括:
◼ 申请和释放不一致
由于 C++ 兼容 C,而 C 与 C++ 的内存申请和释放函数是不同的,因此在 C++ 程
序中,就有两套动态内存管理函数。一条不变的规则就是采用 C 方式申请的内存就用
C 方式释放;用 C++ 方式申请的内存,用 C++ 方式释放。也就是用 malloc/alloc/realloc
方式申请的内存,用 free 释放;用 new 方式申请的内存用 delete 释放。在上述程序
中,用 malloc 方式申请了内存却用 delete 来释放,虽然这在很多情况下不会有问题,
但这绝对是潜在的问题。
◼ 申请和释放不匹配
申请了多少内存,在使用完成后就要释放多少。如果没有释放,或者少释放了就是
内存泄露;多释放了也会产生问题。上述程序中,指针p和pt指向的是同一块内存,却
被先后释放两次。
◼ 释放后仍然读写
本质上说,系统会在堆上维护一个动态内存链表,如果被释放,就意味着该块内存
可以继续被分配给其他部分,如果内存被释放后再访问,就可能覆盖其他部分的信息,
这是一种严重的错误。
c++中的内存泄露问题
badleak1.cpp
#include <iostream> #include <vector> class Item { public: Item() { printf("构造Item\n"); } ~Item() { printf("析构Item\n"); } }; int main(void) { std::vector<Item*> vItem; for(int i= 0; i < 2; i++) { Item *item = new Item(); vItem.push_back(item); } return 0; }
g++ -g -o badleak badleak.cpp -std=c++11
valgrind ./badleak
new 出来的内存,没有delete 释放 ,出现内存泄露,valgrind检测出来了
看看下面这个demo,使用智能指针修饰一下
badleak2.cpp
g++ -g -o badleak badleak2.cpp -std=c++11
valgrind ./badleak2
#include <iostream> #include <vector> #include <memory> using namespace std; class Item { public: Item() { printf("构造Item\n"); } ~Item() { printf("析构Item\n"); } }; int main(void) { vector<shared_ptr<Item>> vitem; for(int i = 0 ;i<2;i++){ shared_ptr<Item> item = shared_ptr<Item>(new Item()); //也可以 shared_ptr<Item> item = make_shared<Item>(); vitem.push_back(item); } return 0; }
问题解决