内存泄漏形象的比喻是“操作系统可提供给所有进程的存储空间正在被某个进程榨干”,最终结果是程序运行时间越长,占用存储空间越来越多,最终用尽全部存储空间,整个系统崩溃。所以“内存泄漏”是从操作系统的角度来看的。这里的存储空间并不是指物理内存,而是指虚拟内存大小,这个虚拟内存大小取决于磁盘交换区设定的大小。由程序申请的一块内存,如果没有任何一个指针指向它,那么这块内存就泄露了。
引起内存泄露的原因主要是,由于使用new关键字在堆中创建对象,或者使用malloc之类的在堆中开辟内存,没有释放连续积累造成的。例如,我们经常有这样的操作,我们右击弹出一个菜单,然后点击一个菜单项,菜单就会消失,当菜单弹出时要开辟内存,那么在菜单消失时,理所当然应该释放内存,否则,在菜单的不断使用过程中,程序的内存占有量会持续上升,直至程序崩溃。记得Duilib的MenuDemo出现过类似的问题,但是后来被修复了。虽然内存泄露很容易被发现,打开任务管理器肉眼观察就行了,但是寻找泄露根源是非常不容易的,代码越是复杂越难找。
Juce为我们提供了LeakedObjectDetector,我自己给它翻译成“对象泄露探测器”,这里得注意措辞,是“对象”泄露探测器,也就是说只能能探测new出来的对象,用malloc等搞出来的内存就不好使了。LeakedObjectDetector的作用是记录对象创建和释放的次数差,当程序退出时,两者平衡了,说明正常,否则就是内存泄露或者重复释放,由于代码比较简单,所以只能定位到是哪个类发生了泄露,想得到再具体的信息就无能为力了。
下面是我经过简单修改后的代码(只是修改了出现异常后的提示信息)
template <class OwnerClass>
class LeakedObjectDetector //Object泄露探测器
{
public:
LeakedObjectDetector() { ++(getCounter().numObjects); } //创建对象时,计数器自加
LeakedObjectDetector (const LeakedObjectDetector&) { ++(getCounter().numObjects); }
~LeakedObjectDetector() //对象析构时,计数器自减,当计数器记录的值为负值时,显然释放的次数大于创建的次数,说明有重复释放现象
{
if (--(getCounter().numObjects) < 0)
{
LPTSTR LeakedMessage = new TCHAR[64];
_stprintf(LeakedMessage,_T("对象类型:%s\n对象个数:%d"),getLeakedObjectClassName(),getCounter().numObjects.getValue());
MessageBox(0,LeakedMessage,L"重复释放",0);
}
}
private:
class LeakCounter //对象计数器
{
public:
LeakCounter() {}
~LeakCounter() //当程序退出时,静态计数器会析构,这时统计当前对象的个数,为泄露对象的个数
{
if (numObjects.get() > 0)
{
LPTSTR LeakedMessage = new TCHAR[64];
_stprintf(LeakedMessage,_T("对象类型:%s\n对象个数:%d"),getLeakedObjectClassName(),numObjects.getValue());
MessageBox(0,LeakedMessage,L"内存泄露",0);
delete LeakedMessage;
}
}
Atomic<int> numObjects; //原子int类型
};
static const LPTSTR getLeakedObjectClassName()
{
return OwnerClass::getLeakedObjectClassName();
}
static LeakCounter& getCounter() //静态计数器
{
static LeakCounter counter;
return counter;
}
};
#define LEAK_DETECTOR(OwnerClass) \
friend class LeakedObjectDetector<OwnerClass>; \
static const LPTSTR getLeakedObjectClassName() { return _T(#OwnerClass); } \
LeakedObjectDetector<OwnerClass> leakDetector##OwnerClass;
于是,我们就可以这样使用了
class A
{
public:
A()
{
}
~A()
{
}
private:
LEAK_DETECTOR(A)
}
当使用A创建的对象,或者A的子类创建的对象发生内存泄露时,就会出现相应的提示信息