内存池--定长内存池

简介

STL的 __pool_alloc, __mt_alloc,boost的pool系列, ace的ACE_Cached_Allocator均为定长内存池。

 

说明

内存池,根据存储的元素的长度是否可变,分为变长,与定长两种内存池。

从逻辑上来讲,定长内存池只需存储相同大小的元素,因此无须花费额外的空间(数据结构)来存储元素长度的信息。

以上几种定长内存池都可以较好的处理定长内存频繁分配的问题。

 

STL--pool_alloc

pool_alloc,原理是挂了16个链表(_S_free_list),每个链表存储分配大小为[1,8],[9-16],[17-24],...,[120,128]的数据。

1.下图(图一)为数据结构:

 1     class __pool_alloc_base
 2     {   
 3     protected:
 4 
 5       enum { _S_align = 8 };
 6       enum { _S_max_bytes = 128 };
 7       enum { _S_free_list_size = (size_t)_S_max_bytes / (size_t)_S_align };
 8 
 9       union _Obj
10       {   
11     union _Obj* _M_free_list_link;
12     char        _M_client_data[1];    // The client sees this.
13       };  
14 
15       static _Obj* volatile         _S_free_list[_S_free_list_size];
16 
17       // Chunk allocation state.
18       static char*                  _S_start_free;
19       static char*                  _S_end_free;
20       static size_t                 _S_heap_size;
21 
22         ... 
23     };

 2.下图(图二)为链表示意图:

 3.allocate

 __pool_alloc<_Tp>::allocate(size_type __n, const void*)

allocate时,该函数会根据请求的__n乘以sizeof(T)来计算,决定由哪个链表分配。

4.deallocate

__pool_alloc<_Tp>::deallocate(pointer __p, size_type __n)

这点需要重点注意,__n必须要传。如果__n的值与allocate分配时的大小不一致,将会导致deallocate失败。原因是__pool_alloc不会保存实际元素有多长,他需要根据__n来计算数据到底存于哪个链表。 

 STL--mt_alloc

mt_alloc实现相对复杂,我本地有一篇详细的文档,但由于篇幅的原因,不适宜在本文中添加。

定义 

template<typename _Tp, 
       typename _Poolp = __common_pool_policy<__pool, __thread_default> >
    class __mt_alloc : public __mt_alloc_base<_Tp>

说明

allocate和deallocate实际都是使用的__pool来分配和管理内存。

实际上,__pool会在freelist上查找是否有free的block;

而1.线程ID(对应的,非原始线程ID) 2.数据大小这两个条件将必然能从多个freelist集合中对应出唯一freelist来操作。

 

  1. 数据大小->bin
  2. 每个bin包含每个线程的freelist。 
因此每个线程在做allocate时,不需要等待,都是在各自的freelist上去操作,这样提高了并发。

 

allocate: 

 

  1 template<typename _Tp, typename _Poolp>

 2 typename __mt_alloc<_Tp, _Poolp>::pointer
 3      __mt_alloc<_Tp, _Poolp>::
 4      allocate(size_type __n, const void*)
 5      {
 6          ...
 7          // Requests larger than _M_max_bytes are handled by operator
 8          // new/delete directly.
 9      
10                // Round up to power of 2 and figure out which bin to use.
11        const size_t __which = __pool._M_get_binmap(__bytes);
12        const size_t __thread_id = __pool._M_get_thread_id();
13      
14        // Find out if we have blocks on our freelist.  If so, go ahead
15        // and use them directly without having to lock anything.
16  
17          ...
18      }

 

deallocate类似,不在此赘述。

 

 

 

 
posted @ 2015-04-07 21:52  Lawrence.Lau  阅读(770)  评论(0编辑  收藏  举报