boost内存池
0x0 导语
最近公司项目中需要用到对象池实现小对象的快速构造和析构,之前项目中使用的是boost::object_pool,但是后面发现boost::object_pool的实现为满足快速连续内存块分配和自动内存回收导致不能很好满足项目效率需求。这是收集对象池相关资料的第一篇,总结boost内存池和对象池。
0x1 boost::pool
关于boost::pool的设计理念和源码分析可以参考博客boost::pool 库速记.
设计理念:
- boost::pool为定长内存池,申请的大块的内存块描述成block,小块内存描述成chunk。
- block逻辑上被划分为3个部分:数据区、指向下个block的指针和下个block长度;
- block通过指针维护成单向block链表,见图1;
- block数据区被划分成next_size个partition_size大小的chunk, 每个chunk的头部存储指向下个chunk的地址,从而形成空闲链表,也就是当chunk空闲时其头部存储下个空闲chunk地址,当分配出去时则完全作为用户数据区,节省了指针的空间,这种设计也造成了一些效率上的问题,见图2。
上面链接的博客boost::pool 库速记对主要的数据结构绘制了非常精美的示意图,链接过来说明下,如果原作者觉得侵权请留言删除。
图1 block形成分配内存块单向链表
图2 block数据区内部形成空闲链表
源码部分boost::pool 库速记也进行了详细的解析,下面主要描述我觉得设计上相关的源码部分:
boost::pool在初始化的时候定义了explicit的构造函数,需要传入用户需要的chunk大小nrequested_size、第一次分配block需要的chunk个数nnext_size和后续使用doubling algorithm增长nnext_size时上限nmax_size。实际chunk大小和最小分配空间min_alloc_size、最小对齐空间min_align有关。
// static_lcm是最小公倍数求取器
BOOST_STATIC_CONSTANT(size_type, min_alloc_size =
(::boost::integer::static_lcm<sizeof(void *), sizeof(size_type)>::value) );
BOOST_STATIC_CONSTANT(size_type, min_align =
(::boost::integer::static_lcm< ::boost::alignment_of<void *>::value, ::boost::alignment_of<size_type>::value>::value) );
///
size_type alloc_size() const
{
size_type s = (std::max)(requested_size, min_alloc_size);
size_type rem = s % min_align;
if(rem)
s += min_align - rem;
return s;
}
boost::pool内部保存block链表的头指针details::PODptr<size_type> list,PODptr是一次性申请的大内存块的封装类,内部维护了内存块头地址ptr和内存块大小sz。boost::pool继承自simple_segregated_storage, simple_segregated_storage负责block划分成chunk,内部维护void* first, 指向空闲链表的头指针。
boost::pool比较核心的4个接口为malloc、free、ordered_malloc和ordered_free。
(1)malloc和free: boost::pool是定长内存池,当用户需要申请一个对象空间时调用malloc。malloc首先会检查当前空闲链表中是否有空闲节点,有则将头结点地址分配出去,没有空间节点则调用malloc_need_resize申请新的block, 插入到block链表头,通过将调用simple_segregated_storage::add_block将新block的数据区切分成chunk链表加入到空闲节点链表中, 如图3所示; free则将chunk插入到当前空闲节点链表中, 如图4所示。所以malloc和free函数都是O(1)的复杂度,效率极高,额外的开销是在新申请block的时候需要切分chunk。