[内存管理]内存池pool库


 pool库概述

如果之前学过操作系统的内存管理机制和内存分配算法等知识,那么就了解“内存池”的概念。

简单地说,内存池预先分配了一块大的内存空间,然后就可以在其中使用某种算法实现高效快速的自定制内存分配。

boost.pool库基于简单分配存储思想实现了一个快速、紧凑的内存池库,不仅能够管理大量的对象,而且还可以被用作STL的内存分配器。它近似于一个小型的垃圾回收机制,在需要大量地分配/释放小对象时很有效,并且完全不需要考虑delete。

pool库包含四个部分,最简单地pool、分配类实例的object_pool、单件内存池singleton_pool和可用于标准库的pool_alloc。

 

1:pool

pool是最简单也最容易使用的内存池类,可以返回一个简单数据类型(POD,Plain Old Data)的内存指针,它位于命名空间boost,为了使用pool组件,需要包含头文件

类摘要:

template
class pool{
     public:
	 explicit pool(size_type requested_size);
	 ~pool();
	 size_type get_requested_size() const;
	 
	 void *malloc();
	 void *ordered_malloc();
	 void *ordered_malloc(size_type n);
	 bool is_from(void *chunk) const;
	 
	 void free(void *chunk);
	 void ordered_free(void *chunk);
	 void free(void *chunks,size_type n);
	 void ordered_free(void *chunks,size_type n);
	 
	 bool release_memory();
	 bool purge_memory();
};


操作函数

pool的模板类型参数UserAllocator 是一个用户定义的内存分配器,它实现了特定的内存分配算法,通常可以直接用默认的default_user_allocator_new_delete。

pool的构造函数接受一个size_type类型的整数requested_size,指示每次pool分配内存块的大小(而不是pool内存池的大小),这个值可以用get_requested_size()获得。pool会根据需要自动地向系统申请或归还使用的内存,在析构时,pool将自动释放它所持有的所有内存块。

成员函数malloc()和ordered_malloc()的行为很类似C中的全局函数malloc(),用void*指针返回从内存池中分配的内存块,大小为构造函数中指定的requested_size。如果内存分配失败,函数将返回0,不会抛出异常。malloc()从内存池中任意分配一个内存块,而ordered_malloc()则在分配的同时合并空闲块链表。ordered_malloc()带参数的形式还可以连续分配n块的内存。分配后的内存块可以用is_from()函数测试是否是从这个内存池分配过去的。

与malloc()对应的一组函数是free(),用来手工释放之前分配的内存块,这些内存块必须是从这个内存池分配出去的(is_from(chunk) == true)。一般情况内存池会自动管理内存分配,不应该调用free()函数,除非你认为内存池的空间已经不足,必须释放已经分配的内存。

最后还有两个成员函数:release_memory()让内存池释放所有未被分配的内存,但已分配的内存块不受影响;purge_memory()则强制释放pool所持有的所有内存,不管内存块是否被使用。实际上,pool的析构函数就是调用的purge_memory()。这两个函数一般情况下也不应该手工调用。

 

使用示例:

#include 
#include 
using namespace boost;
using namespace std;
int main(){
 //一个可分配int的内存池 
  pool<> pl(sizeof(int));
  //必须把void*转换成需要的类型 
  int *p = (int *)pl.malloc();
  assert(pl.is_from(p));
  cout << "pl is allocated from pool." << endl;
  //释放内存池分配的内存块 
  pl.free(p);
  cout << "Free memory pool of the allocated memory block" << endl;
  //连续分配大量的内存 
  for(int i = 0; i < 100; ++i)
  pl.ordered_malloc(10);
  cout << "allocated a large of memory" << endl
       << "now free all memory..." << endl;
     
}


运行结果:

pl is allocated from pool.
Free memory pool of the allocated memory block
allocated a large of memory
now free all memory...

 

pool很容易使用,可以像C中的malloc()一样分配内存,然后随意使用。除非有特殊要求,否则不必对分配的内存调用free()释放,pool会很好地管理内存。

因为pool在分配内存失败的时候不会抛出异常,所以实际编写代码时应该检查malloc()函数返回的指针,以防止空指针错误。不过这种情况极少出现。

int *p = (int *)pl,malloc();

if(p != NULL);

关于pool<>需要注意的是:它只能作为普通数据类型如int、double、float等的内存池,不能应用于复杂的类和对象,因为它只负责分配内存,不调用构造函数。

 

object_pool

object_pool是用于类对象的内存池,它的功能与pool类似,但会在析构时对所有已经分配的内存块调用析构函数,从而正确地释放资源。

类摘要:

 

template
class object_pool:protected pool{
   public:
   object_pool();
   ~object_pool();
   
   element_type *malloc();
   void free(element_type *p);
   bool is_from(element_type *p) const;
   
   element_type * construct();
   void destory(element_type *p);	
};

  操作函数

object_pool是pool的子类,但它使用的是保护继承,因此不能使用pool的接口。

object_pool的模板参数类型参数ElementType指定了object_pool 要分配的元素类型,要求其析构函数不能抛出异常。一旦在模板中指定了类型,object_pool对象就不能再用于分配其他类型的对象。

malloc()和free()函数分别分配和释放一块类型为ElementType*的内存块,同样,可以用is_from()来测试内存块的归属,只有是本内存池分配的内存才能被free()函数释放掉。但它们被调用时并不调用类的构造函数和析构函数,也就是说操作的是一块原始内存块,里面的值是未定义的。因此应该尽量少用malloc()和free()。

object_pool的特殊之处是construct()和destory()函数,这两个函数是object_pool的真正价值之所在。construct()实际上是一组函数,有多个参数的重载形式(目前最多支持三个参数,但可以扩展),它先调用malloc()分配内存,然后再在内存块上使用传入的参数调用类的构造函数,返回的是一个已经初始化的对象指针。destory()则先调用对象的析构函数,然后再用free()释放内存块。

 

使用示例:

#include 
#include 
using namespace std;
using namespace boost;
//一个示范类 
struct demo_class{
   public:
   int a,b,c;
   demo_class(int x = 1,int y = 2,int z = 3):a(x),b(y),c(z){}	
};
int main(){
	//对象内存池 
	object_pool pl;
	//分配一个原始的内存块 
	demo_class *p = pl.malloc();
	assert(pl.is_from(p));
	cout << "ok,allocated from pl..." << endl;
	//p指向的内存未经过初始化 
	assert(p->a != 1 || p->b != 2 || p->c != 3);
	//构造一个对象,可以传递参数 
	p = pl.construct(7,8,9);
	assert(p->a == 7);
	cout << p->a << " " << p->b << " " << p->c << endl;
	//定义一个分配string对象的内存池 
	object_pool pls;
	//连续分配大量string对象 
	for(int i = 0; i < 10; ++i){
	  string *ps = pls.construct("hello object_pool");
	  cout << *ps << endl;
	}
	//所有创建的对象在这里都被正确地析构、释放内存 
}


singleton_pool

singleton_pool与pool的接口完全一致,可以分配简单数据类型的内存指针,但它是一个单件,并提供线程安全。

由于目前Boost还未提供标准的单件库,singleton_pool在其内部实现了一个较简单、泛型的单件类,保证在main()函数运行之前就创建单件。

 

类摘要:

template< typename Tag,unsigned RequestedSize >
class singleton_pool{
    public:
	static bool is_from(void *ptr);
	static void *malloc();
	static void *ordered_malloc();
	static void *ordered_malloc(size_type n);
	
	static void free(void *ptr);
	static void ordered_free(void *ptr);
	static void free(void *ptr,std::size_type n);
	
	static bool release_memory();
	static bool purge_memory();	
};


 singleton_pool主要有两个模板类型参数(其余的可以使用缺省值)。第一个Tag仅仅是用于标记不同的单件,可以是空类,甚至是声明(这个用法还被用于boost.exception)。

第二个参数RequestedSize等同于pool构造函数中的整数requested_size,指示pool分配内存块的大小。

   singleton_pool的接口与pool完全一致,但成员均是静态的,因此不需要声明singleton_pool的对象,直接用域操作符::来调用静态成员函数。因此singleton_pool是单件,所以它的生命周期与整个程序一样长,除非搬运调用release_memory()或者purge_memory(),否则singleton_pool不会自动释放所占用的内存。除了这两点其他用法与pool完全相同。

 

pool_alloc

pool_alloc提供了两个可以用于标准容器模板参数的内存分配器,分别是pool_alloc和fast_pool_allocator,它们的行为与之前的内存池类有一点不同-----当内存分配失败时会抛出异常std::bad_alloc,除非特别的需求,我们应该总使用STL实现自带的内存分配器,使用pool_alloc需要经过仔细测试,以保证它与容器可以共同工作。

使用示例:

#include 
#include 
#include 
using namespace std;
using namespace boost;
int main(){
 //使用pool_allocator代替标准容器默认的内存分配器 
    vector > v;
    //vector将使用新的分配器良好工作 
 v.push_back(10);
 cout << v.size() << endl; 
}

你可能感兴趣的:(MFC/STL/Boost)