防止内存泄漏的方法
- 内部封装:将内存的分配和释放封装到类中,在构造的时候申请内存,析构的时候释放内存。 C++
#include <iostream>
#include <cstring>
using namespace std;
class A
{
private:
char *p;
unsigned int p_size;
public:
A(unsigned int n = 1) // 构造函数中分配内存空间
{
p = new char[n];
p_size = n;
};
~A() // 析构函数中释放内存空间
{
if (p != NULL)
{
delete[] p; // 删除字符数组
p = NULL; // 防止出现野指针
}
};
char *GetPointer()
{
return p;
};
};
void fun()
{
A ex(100);
char *p = ex.GetPointer();
strcpy(p, "Test");
cout << p << endl;
}
int main()
{
fun();
return 0;
}
程序运行结果:
count is : 1
Test
count is : 2
ex1.p = Test
count is : 1
count is : 0
buf is deleted
注:程序运行结果的倒数 2、3 行是调用两次析构函数时进行的操作,在第二次调用析构函数时,进行内存空间的释放,从而会有倒数第 1 行的输出结果。
- 智能指针 智能指针是C++ 中已经对内存泄漏封装好了一个工具,可以直接拿来使用。
valgrind 是一套 Linux 下,开放源代码(GPL V2)的仿真调试工具的集合,包括以下工具:
- Memcheck:内存检查器(valgrind 应用最广泛的工具),能够发现开发中绝大多数内存错误的使用情况,比如:使用未初始化的内存,使用已经释放了的内存,内存访问越界等。
- Callgrind:检查程序中函数调用过程中出现的问题。
- Cachegrind:检查程序中缓存使用出现的问题。
- Helgrind:检查多线程程序中出现的竞争问题。
- Massif:检查程序中堆栈使用中出现的问题。
- Extension:可以利用 core 提供的功能,自己编写特定的内存调试工具。
Memcheck 能够检测出内存问题,关键在于其建立了两个全局表:
-
Valid-Value 表:对于进程的整个地址空间中的每一个字节(byte),都有与之对应的 8 个 bits ;对于 CPU 的每个寄存器,也有一个与之对应的 bit 向量。这些 bits 负责记录该字节或者寄存器值是否具有有效的、已初始化的值。
-
Valid-Address 表:对于进程整个地址空间中的每一个字节(byte),还有与之对应的 1 个 bit,负责记录该地址是否能够被读写。
检测原理:
- 当要读写内存中某个字节时,首先检查这个字节对应的 Valid-Address 表中对应的 bit。如果该 bit 显示该位置是无效位置,Memcheck 则报告读写错误。
- 内核(core)类似于一个虚拟的 CPU 环境,这样当内存中的某个字节被加载到真实的 CPU 中时,该字节在 Valid-Value 表对应的 bits 也被加载到虚拟的 CPU 环境中。一旦寄存器中的值,被用来产生内存地址,或者该值能够影响程序输出,则 Memcheck 会检查 Valid-Value 表对应的 bits,如果该值尚未初始化,则会报告使用未初始化内存错误。