对象内存池

好的内存管理不仅仅可以提高程序的性能(尤其是对程序频繁申请释放内存的地方进行优化),而且可以提高程序的健壮性(相信人们对时不时发生内存非法访问而产生的crash深恶痛绝)。

提到内存管理就不得不提内存池,现行的所有操作系统都有自己高效的内存池管理机制。这里的所谓高效有两个方面:一个就是提高程序访问内存的速度;另一个是提高内存的使用率,尽可能的减少内存碎片。《C++应用程序性能优化》一书中写的那个内存池实例就是一种经典的内存池实现,据说windows系统就有过类似的设计。这种内存池的设计需要预先设定内存的划块方法,每种方法都把内存切分成不同的粒度,根据程序分配内存的需要找到合适的切分粒度(合适的切分粒度可以使得产生的内存碎片尽量少),然后把用到的内存挂到一个链表中,释放的内存挂到另一个空闲链表中;所有的申请、释放操作就在这个切分粒度和这两个链表中捣鼓。

上述的这种内存池管理无疑很通用,使用起来也很简单;但是,在我们的一些实际应用中也不一定需要设计这么复杂的内存池管理机制。我的想法是:

1. 程序中并不一定需要将所有的内存全部都有内存池管理;尤其是一个已经在运行的而又没有进行内存池管理的项目,你想把它改成用内存池管理的时候。

2. 程序中需要进行内存池管理的类其实不会太多,关注一下构造析构执行频率很高的类就会对性能有很大的帮助。

所以最简单粗暴的方法莫过于为每个类建立一个pool,这个类的所有对象都丢到这个pool中。不要鄙视这个方法,据说大名鼎鼎的DOOM中的对象管理就是这么搞的。我用模板搞了一个,具体的思路如下:

1 template<typename TClass>
2 class ObjPool{
3 struct Chunk{
4 TClass* data;
5 Chunk* next;
6 };
7
8 Chunk* ch_using_head; //正在用的对象
9 Chunk* ch_free_head; //空闲链表
10
11 public:
12 ObjPool();
13 ~ObjPool();
14
15 void* Alloc();
16 void Free(void* ptr);
17
18 };

其中的Alloc函数负责分配一个对象,Free负责释放一个对象;具体的就很简单,利用malloc和Placement new去做就好了;值得注意的是用Placement new分配的对象释放的时候需要手动调用析构函数:(TClass*)obj->~TClass();

写好这个模板之后,具体代码中的应用就是这样的:

1 #include "ObjPool.h"
2
3 //声明一个类的内存池
4 class A;
5 ObjPool<A> pool_A;
6
7 //类定义
8 class A
9 {
10 int data[100];
11
12 public:
13 A(){};
14 ~A(){};
15
16 void* operator new(size_t size)
17 {
18 if(size != sizeof(A))
19 return ::operator new(size);
20 return pool_A.Alloc();
21 }
22
23 void operator delete(void* ptr)
24 {
25 if(ptr)
26 return pool_A.Free(ptr);
27 }
28 }

恩,看起来十分繁琐;如果每个地方,每个类都要这么去写,相信所有人都会很郁闷;但是可以看出这里的代码都是重复性的,自然可以用宏来解决;我们在ObjPool.h中定义两个宏,如下:

1 #define REGIST_CLASS_POOL(class_name) \
2 class class_name; \
3 static ObjectPool<class_name> pool_##class_name;
4
5 #define IMPLEMENT_CLASS_POOL(class_name) \
6 public: \
7 void* operator new(size_t size) \
8 { \
9 if(size != sizeof(class_name)) \
10 return ::operator new(size); \
11 return pool_##class_name.Alloc(); \
12 } \
13 void operator delete(void* ptr) \
14 { \
15 if(ptr) \
16 pool_##class_name.Free(ptr); \
17 } \
18 private:

这样,在使用的时候就非常方便了:

1 #include "ObjPool.h"
2
3 //声明一个类的内存池
4 REGIST_CLASS_POOL(A)
5
6 //类定义
7 class A
8 {
9 IMPLEMENT_CLASS_POOL(A)
10
11 int data[100];
13 public:
14 A(){};
15 ~A(){};
16 }

是不是很眼熟,恩,我也觉得,跟MFC的写法神似。

还有一点,这个不是现成安全的,要修改成线程安全的,只需要在模板参数中带上锁就OK了。

posted on 2011-03-17 21:07  konlil  阅读(789)  评论(0编辑  收藏  举报

导航