一、What?
内存泄漏:由于程序员的疏忽或错误造成程序未能释放已不在使用的内存,导致减少可用内存,降低计算机性能,导致程序崩溃。
内存溢出:是指已有的数据超过了其获得到的内存所能存储的范围,比如用一个字节存放1000这个数字就属于内存溢出。
int *p = new int;
return 0;
new之后没有delete,那么return后就会发生内存泄露。
虽然看上去这样并不造成什么严重后果,但是
while(true)
{
int *p = new int;
}
return 0;
这种也是人为错误,甚至会导致程序崩溃。
而在代码量很大的情况下并不好检查,所以需要使用工具来进行内存泄漏的检测,辅助我们找到内存泄漏代码的地方:某某文件、某某行。
二、How?
一种简单的检测方法:
1、原理:
C++有操作符重载的机制,所以可以通过重载 new、delete 操作符,来做一些处理。
每次执行 new 操作时,将指针和当前文件名称和行号保存在我们自己维护的一个容器里;执行 delete 时,将对应指针的项从容器中删除,最后程序结束时检查容器中是否还有未释放的指针。
2、实现:
(1)new操作符重载
#define new new(__FILE__,__LINE__)
void* operator new(size_t size, const char* file, long line)
{
void* p = malloc(size);
mch.Add(p, file, line); //往容器中添加项
return p;
}
(2)delete操作符重载
void operator delete(void* p)
{
mch.Remove(p); //删除容器中对应指针
free(p);
}
测试之后出现两个Bug
测试1::
new之后未释放结果检测内存泄漏了
测试2:
new 之后明明 delete 了,为什么还会出现内存泄漏呢?
原因是 new 了数组后执行的是 delete[] ,而重载的是 delete,所以容器里面指针的并没有删除,那么就需要重载 delete[]
void operator delete[](void* p)
{
mch.Remove(p);
free(p);
}
测试3:
在前面两个测试中也出现了这样的异常崩溃。
首先,程序在加载时:
a、将程序加载到系统内存;
b、将原有 new 和 delete 全部替换;
c、程序需要执行 new 和 delete 的地方都被监管;
最后,程序卸载:
a、将程序中的对象析构;
b、对象被析构之后以及不存在;
c、此时 delete 和 new 里面还在使用这个对象,所以造成异常崩溃。
所以在类中添加一个 static bool Ready;构造函数初始化它为 true,析构函数让它为 false,在对象存在的时候才可以 new 和 delete。
#include "MemChecker.h"
MemChecker mch;
bool MemChecker::Ready = false;
void* operator new(size_t size, const char* file, long line)
{
void* p = malloc(size);
if (MemChecker::Ready)
mch.Add(p, file, line);
return p;
}
void operator delete(void* p)
{
if (MemChecker::Ready)
mch.Remove(p);
free(p);
}
void operator delete[](void* p)
{
if (MemChecker::Ready)
mch.Remove(p);
free(p);
}
MemChecker::MemChecker()
{
Ready = true;
}
MemChecker::~MemChecker()
{
Dump();
Ready = false;
}
void MemChecker::Add(void* pointer, const char* file, long line)
{
pointer_map_[pointer] = Entry(file, line);
}
void MemChecker::Remove(void* pointer)
{
auto iter = pointer_map_.find(pointer);
if (iter != pointer_map_.end())
{
pointer_map_.erase(iter);
}
}
void MemChecker::Dump()
{
if (!pointer_map_.empty())
{
cout << "内存泄露" << endl;
for (auto iter = pointer_map_.begin(); iter != pointer_map_.end(); ++iter)
{
const char* file = iter->second.File();
long line = iter->second.Line();
cout << "在" << file << "\t" << line << "出现内存泄露" << endl;
}
}
}