头文件 pool.h

先定义引用计数基类 Ref 与自动释放池 autoreleasePool。Ref 持有一个 _RefCount 计数器,支持 retain / release / autorelease 三种操作。autoreleasePool 用单例模式管理所有延迟释放的对象,并在 clear 时统一 release。

这里特别要注意:~Ref() 必须声明为 virtual,否则通过基类指针删除子类对象时,子类部分无法析构,会造成内存泄露。

pool.h
                
                
                
                #include <iostream>
                #include"stack"
                #include "vector"
                using namespace std;
                class autoreleasePool;
                
                
                class Ref
                {
                protected:
                	unsigned int _RefCount;
                
                public:
                	Ref() :_RefCount(1){}
                
                	virtual ~Ref()/*必须virtual 不然子类部分不能析构 导致内存泄露*/
                	{
                		cout<<"~ref"<<endl;
                	}
                
                	void retain();
                	void release();
                
                	void autorelease();
                };
                
                class autoreleasePool
                {
                public:
                	static autoreleasePool*_autoreleasePool;
                
                
                
                	autoreleasePool(){}
                	~autoreleasePool(){ data.clear(); }
                	static autoreleasePool*getInstance()
                	{
                		if(_autoreleasePool==nullptr)
                			_autoreleasePool = new autoreleasePool;
                
                		return _autoreleasePool;
                	}
                
                	static  void destoryInstance()
                	{
                		if (_autoreleasePool != nullptr)
                		{
                			delete _autoreleasePool;
                			 _autoreleasePool=nullptr;
                		}
                	}
                
                
                	vector<Ref*> data;
                
                
                	void clear()
                	{
                		for (int i = 0;i<data.size();i++)
                		{
                			data[i]->release();
                		}
                		data.clear();
                		data.reserve(10);
                	}
                	void addObject(Ref*add)
                	{
                		data.push_back(add);
                	}
                
                };
                
                
                 autoreleasePool* autoreleasePool::_autoreleasePool = nullptr;
                
                
                class TTF :public Ref
                {
                	public:
                	long long testData[1000];
                	string name;
                	TTF(string name)
                	{
                		this->name = name;
                		cout << name.c_str()<<" create"<<endl;
                	}
                
                	~TTF()
                	{
                		cout << name.c_str() << " release"<<endl;
                	}
                public:
                	static TTF*create(string name)
                	{
                		return new TTF(name);
                	}
                };

实现文件 pool.cpp

Ref::retain / Ref::release 分别负责计数器加一和减一,减到 0 时 delete this。Ref::autorelease 把自身塞入全局自动释放池,留待后续 clear 一次性回收。

run 函数用于压力测试:循环两轮,每轮创建 times 个 TTF 对象并全部放入自动释放池,随后 Sleep(100) 观察内存占用,再调用 clear 统一释放。

pool.cpp
                
                
                
                
                #include "pool.h"
                #include "windows.h"
                #include "stdlib.h"
                
                void Ref::retain()
                {
                	++_RefCount;
                }
                
                void Ref::release()
                {
                	--_RefCount;
                	if (_RefCount == 0)
                	{
                		delete this;
                	}
                }
                
                void Ref::autorelease()
                {
                	autoreleasePool::getInstance()->addObject(this);
                }
                
                
                void run(int times)
                {
                
                	char str[100];
                
                	int i = 2;
                
                	while (i)
                	{
                		--i;
                		for (int x = 0; x < times; x++)
                		{
                			sprintf(str, "count is %d", x);
                			auto tmp = TTF::create(str);
                			tmp->autorelease();
                		}
                		cout << "after 100ms  all TTF's objects will be released  " << endl;
                		Sleep(100);
                
                		autoreleasePool::getInstance()->clear();
                
                		Sleep(100);
                
                	}
                
                
                }

入口 main

int main(int argc, char *argv[])
                {
                
                	run(5000);
                	system("pause");
                	return 0;
                }

测试中的内存现象

用上面的代码跑 run(5000),观察任务管理器里的内存占用:程序刚启动时只有 0.4m,运行中峰值冲到 78.6m,跑完所有循环后依然占用 3.6m。

问题来了:为什么结束后还会占用 3.6m?用内存泄露检测工具跑了一遍,并没有检测到泄露。更神奇的是,用系统自带的内存清理工具清理一下,占用立刻回落到正常水平。

作为对比,再做一次测试二:不走自动释放池(也就是不延后释放),对象一创建出来就马上 release,这种方式下内存占用表现"正常",不会出现前面那种 3.6m 残留。

猜测与讨论

结合两次测试结果,基本可以猜测:当进程短时间内大量 new 内存、再批量 delete 时,操作系统并不会马上把这些内存完全收回,而是会预留一部分给当前进程,用来加速下一次 new 的执行速度——毕竟内存操作相对比较费时。

和操作系统老师讨论过这个现象,他也倾向于这个解释。只是 Windows 本身并不开源,具体实现只能停留在猜测层面。

over ~~~