一、内存泄漏
1、定义
内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
2、出现场景
1.忘记释放内存
- 场景描述:程序在堆上分配内存但在不需要的时候忘记释放
int *ptr = new int;
//使用ptr......
//忘记delete ptr;
2.双重释放
- 场景描述:同一块内存被释放了两次或多次,这通常会导致程序崩溃或未定义行为。
int *ptr = new int;
delete ptr;
//误删除
delete ptr;//重复删除导致未定义行为
3. 构造函数与析构函数不匹配
- 场景描述:在类的构造函数中动态分配了内存,但在析构函数中未释放或未正确释放这些内存。
Class MyClass{
public:
MyClass()
{
data = new int[10];
}
~ MyClass()
{
//忘记delete[] data
}
private:
int *data;
}
4. 静态变量和全局对象
场景描述:静态变量和全局对象在程序的生命周期内都存在,如果它们指向动态分配的内存,并且这些内存没有在程序结束前被释放,就会发生内存泄漏。
static MyClass* global = new MyClass();
//...其他代码...
//程序结尾,但是global指向的内存未被释放
5. 异常处理不当
- 场景描述:在异常处理代码中,如果捕获了异常但没有释放之前分配的内存,就会导致内存泄漏。
Void MyFunction()
{
MyClass* global = new MyClass();
try()
{
//抛出异常代码
}
catch(...)
{
//忘记delete global;
thow;重新抛出异常
}
//如果没有异常,则会释放内存
delete global;
}
6. 第三方库资源未释放
- 场景描述:使用第三方库时,如果没有正确释放由库分配的资源,也会导致内存泄漏。
void MyFunction()
{
SomeLibraryHandle handle = SomeLibrary_AllocateResource();
// 使用资源...
// 忘记调用SomeLibrary_FreeResource(handle)来释放资源
}
7. 循环或递归中的内存分配
- 场景描述:在循环或递归中分配内存,但忘记在每次迭代或递归调用后释放它,也会导致内存泄漏。
void MyFunction()
{
for(int i =0;i <1000;i++)
{
MyClass* global = new NyClass();
//使用...global...
//忘记delete global;
}
//循环结束后大量内存未被释放
}
8. 野指针和悬垂指针
- 场景描述:野指针是指向已被释放或未分配内存的指针,悬垂指针是指向已被释放对象的指针。这两种指针都可能导致内存泄漏,因为它们可能指向无效的内存区域。
int* ptr = new int;
delete ptr;
ptr = nullptr; // 正确的做法是将ptr置为nullptr,避免成为野指针
// 如果未将ptr置为nullptr,且后续又尝试使用ptr,则可能导致未定义行为
二、解决方法
- 使用智能指针:如
std::unique_ptr
和std::shared_ptr
,它们可以自动管理内存释放。 - 仔细处理异常和错误情况:确保在异常或错误发生时释放已分配的内存。
- 使用内存调试器:如Valgrind,帮助检测和解决内存泄漏问题。
- 避免静态变量和全局对象:如果可能,尽量使用局部变量,并在不再需要时释放它们。
- 定期释放内存:在不再需要特定内存块