内存管理之内存池
1. pool 库概述
boost.pool 库基于简单分隔存储的思想实现了一个快速、紧凑的内存池库,不仅能够管理大量的对象,还可以用作 STL 的内存分配器。
在某种程度上讲,它类似一个小型的垃圾回收机制,在需要大量分配/释放小对象时其效率很高,而且完全不需要考虑delete。
2. pool
pool是最简单、最容易使用的内存池类,其可以返回一个简单数据类型的内存指针。其需要包含的头文件如下<boost/pool/pool.hpp>。
2.1 类摘要
pool的类摘要如下:
template<typenameUserAllocator=default_user_allocator_new_delete>classpool{public:// 构造函数和析构函数explicitpool(size_type requested_size);~pool();// 分配内存块的大小size_typeget_requested_size()const;// 分配内存void*malloc();void*ordered_malloc();void*ordered_malloc(size_type n);boolis_from(void*chunk)const;// 归还内存voidfree(void*chunk);voidordered_free(void*chunk);voidfree(void*chunk,size_type n);voidordered_free(void*chunk,size_type n);// 释放内存boolrelease_memory();boolpurge_memory();};2.2 操作函数
pool的模板类型参数UserAllocator是一个用户定义的内存分配器,它实现了特定的内存分配算法,通常可以直接用默认的default_user_allocator_new_delete,它使用new[]和delete[]分配内存。
pool的构造函数可以接收一个size_type类型的整数requested_size,指示每次分配内存块的大小,这个值可以通过get_requested_size()获得。pool会根据需要自动地向系统申请或归还使用的内存,在析构时,pool将自动释放它所持有的所有内存。
成员函数malloc()和ordered_malloc()的行为类似 C 中的全局函数malloc()。malloc()从内存池中任意分配一个内存块,而ordered_malloc()则在分配的同时合并空闲块链表。ordered_malloc()带参数的形式可以连续分配 n 块内存。可以使用is_from()函数测试分配后的内存块是否从这个内存池分配出去的。
free()用于手动释放之前分配的内存块,这些内存块必须是从这个内存池分配出去的。
release_memory()让内存池释放所有未被分配的内存,但已分配的内存块不受影响;purge_memory()则强制释放pool持有的所有内存。
2.3 用法
- 代码示例
#include<iostream>#include<boost/pool/pool.hpp>// pool.cppintmain(){boost::poolpl(sizeof(int));// 一个可分配 int 的内存池// 必须把 void* 转换成需要的类型int*p=static_cast<int*>(pl.malloc());// 判断是否分配成功if(p!=nullptr){// 判断 p 是否来自于 plif(pl.is_from(p)){std::cout<<"内存块 p 来自 pl"<<std::endl;}}// 释放内存池分配的内存块pl.free(p);// 连续分配大量的内存for(inti=0;i<100;++i){pl.ordered_malloc(10);}return0;}- 编译运行
# 编译g++-opool_demo pool.cpp# 运行./pool_demopool只能作为普通类型(如 int、double 等)的内存池,不能将其应用于复杂的类和对象,因为它只分配内存,不调用构造函数。调用构造函数时,我们需要使用object_poll。
3. object_pool
object_pool是用于类实例(对象)的内存池,它的功能与pool类似,但会在析构时对所有已分配的内存块调用析构函数。其包含在头文件<boost/pool/object_pool.hpp>。
3.1 类摘要
obejct_pool的类摘要如下:
template<typenameT,typenameUserAllocator>classobject_pool:protectedpool<UserAllocator>{public:typedefT element_type;object_pool();~object_pool();// 分配内存和归还内存element_type*malloc();voidfree(element_type*p);boolis_from(element_type*p)const;// 创建对象 和 销毁对象element_type*construct(...);voiddestory(element_type*p);};3.2 操作函数
object_pool是pool的子类,但它使用的是保护继承,因此不能使用pool的接口。
object_pool的特殊之处是construct()和destory()函数。construct()实际上是一组函数,有多个参数的重载形式,其先调用malloc分配内存,再在内存块上使用传入的参数调用类的构造函数,返回的是一个已经初始化的对象指针。destroy()则先调用对象的析构函数,再用free释放内存块。
这些函数都不会抛出异常,如果内存分配失败,将返回 0(空指针)
3.3 用法
我们可以像pool那样使用object_pool分配原始内存块,也可以使用construct()直接在内存池中创建对象。
- 代码示例
#include<iostream>#include<boost/pool/object_pool.hpp>// object_pool.cpp// 示范用的类structdemo_class{public:inta,b,c;demo_class(intx=1,inty=2,intz=3):a(x),b(y),c(z){}};intmain(){// 对象内存池boost::object_pool<demo_class>pl;autop=pl.malloc();assert(pl.is_from(p));// p 指向的内存未经过初始化assert(p->a!=1||p->b!=2||p->c!=3);// construct 构造对象,可以传递参数p=pl.construct(7,8,9);std::cout<<p->a<<" "<<p->b<<" "<<p->c<<std::endl;// 定义一个分配 string 对象的内存池boost::object_pool<std::string>pls;// 连续分配大量 string 对象for(inti=0;i<10;++i){std::string*ps=pls.construct("hello object_pool");std::cout<<*ps<<std::endl;}return0;}- 编译运行
# 编译g++-oobject_pool_demo object_pool.cpp# 运行./object_pool_demo4. singleton_pool
singleton_pool与pool的接口完全一致,可以用它分配简单数据类型(POD)的内存指针,但它是一个“单件”。
“单件”指的是单例模式,
singelton_pool不需要创建对象,其把所有的接口都变成了静态函数,整个程序全局只有一个内存池实例。
singleton_pool默认使用 boost.thread 库提供线程安全保证,所以需要将它链接 boost_thread 库,如果不使用多线程,那么可以在头文件前
定义宏BOOST_POOL_NO_MT。
4.1 类摘要
singleton_pool的类摘要如下所示:
template<typenameTag,unsignedRequestedSize>classsingleton_pool{public:staticboolis_from(void*ptr);// 分配内存staticvoid*malloc();staticvoid*ordered_malloc();staticvoid*ordered_malloc(size_type n);// 归还内存staticvoidfree(void*ptr);staticvoidordered_free(void*ptr);staticvoidfree(void*ptr,std::size_t n);staticvoidordered_free(void*ptr,std::size_t n);// 释放内存staticboolrelease_memory();staticboolpurge_memory();};4.2 用法
singelton_pool和pool的接口完全一致,但其成员函数是静态的,所以不需要声明singelton_pool的实例,直接使用域操作符::来调用静态
成员函数即可。
- 代码示例
#include<iostream>#include<boost/pool/singleton_pool.hpp>// 用于标记的空类structpool_tag{};// 第一个参数 Tag 用于标记不同的单件,可以是空类// 第二个参数 RequestedSize 等同于 pool 构造函数中的 requested_size,可以指示 pool 分配的内存块的大小typedefboost::singleton_pool<pool_tag,sizeof(int)>spl;// 定义内存池intmain(){// 分配一个整数内存块int*p=(int*)spl::malloc();// 释放内存spl::release_memory();return0;}- 编译运行
# 编译g++-osingelton_pool_demo singelton_pool.cpp# 运行./singelton_pool_demo