目录
一、内存泄漏
一、介绍
- 内存泄漏,是指在程序代码中动态申请的、堆上的内存 由于某种原因、在使用后没有被释放,进而造成内存的浪费。
- 少部分的内存泄漏不会影响程序的正常运行,不过如果是持续的内存泄漏会耗光系统内存,最终会导致程序卡死甚至系统崩溃。为了避免系统崩溃,在无法申请到内存的时候,要果断调用exit()函数主动杀死进程,而不是试图挽救这个进程。
二、几种内存泄露的场景
1、malloc/new申请的内存没有主动释放
void test1(){char* str = new char[100];/*delete[] str; 这里忘记delete了 */}2、new与free混用,malloc与delete混用
class Base{public:int* values;Base() { values = new int[100]; }~Base() { delete[] values; }};void test2(){Base* pBase = new Base;free(pBase);/* 错误,这样只会释放pBase指向的内存,却不会调用Base的析构函数会导致Base中的values指向的内存无法被释放 */delete pBase; /* 正确 */}3、使用new开辟数组时,delete忘记加[]
void test3(){int* vects = new int[100];delete vects; // 这种写法是错误的delete[] vects; // 这种写法是正确的}4、基类的析构函数没有定义为虚函数
class A{public:A() {}~A() {}};class B : public A{public:B() { num = new int[100]; }~B() { delete[] num; }private:int* num;};void main() {A* pa = new B();delete pa;/* 这样只会调用A类的析构函数,而B类中的num就得不到释放正确的是应该把A的析构类声明为虚函数 */}
三、预防与排查
1、valgrind
- 应用环境:Linux
- 编程语言:C/C++
- 使用方法: 编译时加上-g选项,如 gcc -g filename.c -o filename,使用如下命令检测内存使用情况:
- 结果输出:#valgrind --tool=memcheck --leak-check=yes --show-reachable=yes ./filename,就会看到内存使用报告
- 设计思路:根据软件的内存操作维护一个有效地址空间表和无效地址空间表(进程的地址空间)
- 优缺点:能够检测:
- 使用未初始化的内存 (Use of uninitialised memory)
- 使用已经释放了的内存 (Reading/writing memory after it has been free’d)
- 使用超过 malloc分配的内存空间(Reading/writing off the end of malloc’d blocks)
- 对堆栈的非法访问 (Reading/writing inappropriate areas on the stack)
- 申请的空间是否有释放 (Memory leaks – where pointers to malloc’d blocks are lost forever)
- malloc/free/new/delete申请和释放内存的匹配(Mismatched use of malloc/new/new [] vs free/delete/delete [])
- src和dst的重叠(Overlapping src and dst pointers in memcpy() and related functions)
- 重复free
- 如何获取:http://valgrind.org/
2、使用智能指针
void test4(){// 以下两种写法效果相同shared_ptr<int> p1 = shared_ptr<int>(new int(234));shared_ptr<int> p2 = make_shared<int>(234);// 还有其他指针指针weak_ptr<int> p3 = weak_ptr<int>(p1);unique_ptr<int> p4 = unique_ptr<int>(new int(234));auto_ptr<int> p5 = auto_ptr<int>(new int(234));}
二、内存越界
一、介绍
- 内存越界是软件系统主要错误之一,其后果往往不可预料且非常严重。更麻烦的是,它出现的时机是随机的,表现出来的症状是随机的,而且造成的后果也是随机的,这会使程序员很难找出这些 Bug 的现象和本质之间的联系,从而给 Bug 的定位带来极大的困难。一般情况下,内存越界访问可分如下两种:
- 读越界,即读了不属于自己的数据。如果所读的内存地址是无效的,程序立刻崩溃;如果所读内存地址是有效的,在读的时候不会马上出现问题,但由于读到的数据是随机的,因此它会造成不可预料的后果。
- 写越界,又称为缓冲区溢出,所写入的数据对别的程序来说是随机的,它也会造成不可预料的后果。
二、几种内存越界的情况
- 定义指针的时候未初始化,所以指针指向的时一块随机值,用户并不一定有访问权限。
- 分配到的内存比实际上使用的内存要小。
- 使用下标访问数组时,下标错误。
- 内存已经被释放了,但仍指针来使用这块内存。
三、预防与排查
- 定义指针变量的时候,如果暂时不初始化,可以先让它指向 nullptr。
- 分配内存时,用户要检查内存是否分配成功。
- 使用下标访问数组的时候,要检查是否越界。
- 内存被释放之后,指向这块内存的指针也要赋值为 nullptr。