std::alloc空间配置器
头文件<memory>
1.1 std::allocate、std::deallocate (通过::operator new 和 ::operator delete封装实现)
1.2 std::construct、std::destory (通过placement new 和调用析构函数实现)
template<class T1, class T2> inline void h_construct(T1* p, const T2& value) { new(p) T1(value); } template<class T> inline void h_destroy(T* pointer) { pointer->~T(); }
1.3 std::alloc
STL考虑到小型内存区块问题,存在两个问题:1) 造成内存碎片
2)造成额外负担(索取内存块,需要额外空间用于管理,区块越小,额外负担所占比例越大)
设计了双层级配置器,第一级配置直接使用malloc()和free();第二级配置器则视情况采用不同的策略,当配置区大于128bytes时,直接调用第一级配置器;当配置区块小于128bytes时,便不借助第一级配置器,而使用一个memory pool来实现。究竟是使用第一级配置器还是第二级配置器,由一个宏定义来控制。SGI STL中默认使用第二级配置器。
第二级配置器维护16个free-lists, 各自管理大小为8,16,24...120,128bytes的小额区块。
free-lists节点结构如下,通过union的方式解决了管理节点指针的额外负担,第一个字段构成链表,第二个字段指向实际区块
union obj { union obj* free_list_link; char client_data[1]; /* The client sees this. */ };
类构造
class h_alloc { private: enum { ALIGN = 8, // 对齐边界 MAX_BYTES = 128, // 最大上限 NFREELISTS = MAX_BYTES/ ALIGN // 个数 }; union obj { // 区块对应自由链表的节点结构 union obj* free_list_link; char client_data[1]; /* The client sees this. */ }; private: static size_t round_up(size_t bytes) // 对齐函数 { return (((bytes)+(size_t)ALIGN - 1) & ~((size_t)ALIGN - 1)); } // 计算使用第n号链表,n从0开始 static size_t freeList_index(size_t bytes) { return (((bytes)+(size_t)ALIGN - 1) / (size_t)ALIGN - 1); } // 返回一个大小为n的对象,并可能加入大小为n的其他区块到free_list static void* refill(size_t n); // 配置个大块空间,可容纳nobjs个size大小的区块 static char* chunk_alloc(size_t size, int& nobjs); public: static void* allocate(size_t n); static void deallocate(void* p, size_t n); //static void* reallocate(void* p, size_t old_sz, size_t new_sz); private: static obj* volatile m_free_list[NFREELISTS];// 16个 free-lists static char* m_start_free; //内存池起始位置,只在chunk_alloc()中变化 static char* m_end_free; //内存池结束位置,只在chunk_alloc()中变化 static size_t m_heap_size; }; char* h_alloc::m_start_free = 0; char* h_alloc::m_end_free = 0; size_t h_alloc::m_heap_size = 0; typename h_alloc::obj* volatile h_alloc::m_free_list[NFREELISTS];
1) allocate()函数
void* h_alloc::allocate(size_t n) { obj* volatile * my_free_list; obj* result; if (n > MAX_BYTES) //大于128字节调用第一级配置器 { return (malloc(n)); } else { //寻找16个自由链表中合适的一个 my_free_list = m_free_list + freeList_index(n); result = *my_free_list; if (result == 0) { // 没找到可用的free_list, 重新填充free_list void *r = refill(round_up(n)); return r; } // 调整free_list *my_free_list = result->free_list_link; return result; } }
2) deallocate()
void h_alloc::deallocate(void* p, size_t n) { obj *q = (obj*)p; obj* volatile * my_free_list; if (n > MAX_BYTES) { free(q); return; } // 寻找对应的free_list my_free_list = m_free_list + freeList_index(n); // 调整free_list, 回收区块 q->free_list_link = *my_free_list; *my_free_list = q; }
3) refill()
填充自由链表用,缺省一次取20个新节点,具体看内存池大小
void* h_alloc::refill(size_t n) { int nobjs = 20; //默认取20个节点 char* chunck = chunk_alloc(n, nobjs);//从内存池申请内存 if (nobjs == 1) { return chunck; // 如果只申请到一个节点,不用调整free_list,直接返回 } obj* volatile * m_free_list = m_free_list + freeList_index(n); //找到n号链表 obj* result = (obj*)chunck; // 保存要返回的结果 obj* next_obj = (obj*)(chunck + n); *m_free_list = next_obj; //链表第一个节点 // 串联各个节点,第一个节点已经返回出去了 obj* current_objs; for (int i = 1; ;i++) { current_objs = next_obj; if (nobjs - 1 == i) { current_objs->free_list_link = 0; break; } else { next_obj = (obj*)(next_obj + n); current_objs->free_list_link = next_obj; } } return result; }
4) chunk_alloc()
从内存池取空间给refill函数,内存池不够,从堆空间获取内存给内存池
char* h_alloc::chunk_alloc(size_t size, int& nobjs) { size_t total_bytes = size * nobjs; size_t left_bytes = m_end_free - m_start_free; char* result; if(left_bytes > total_bytes) //内存池剩余空间满足需求量 { result = m_start_free; m_start_free += total_bytes; return result; } else if (left_bytes > size) //内存池剩余空间不满足需求量,但是足够一个以上的区块 { nobjs = left_bytes / size; total_bytes = nobjs * size; result = m_start_free; m_start_free += total_bytes; return result; } else //内存池大小不够一个区块 { size_t bytes_to_get = 2 * total_bytes + round_up(m_heap_size >> 4); //size_t bytes_to_get = 2 * total_bytes; if (left_bytes > 0) //残余零头不浪费 { obj* volatile * m_free_list = m_free_list + freeList_index(left_bytes); //找到n号链表 ((obj*)m_start_free)->free_list_link = *m_free_list; *m_free_list = (obj*)m_start_free; } //申请堆空间 m_start_free = (char*)malloc (bytes_to_get); if (0 == m_start_free) { printf("chunk_alloc malloc failed\n"); //申请失败处理,省略。。。 } m_heap_size += bytes_to_get; m_end_free = m_start_free + bytes_to_get; //递归调用,修正nobjs return (chunk_alloc(size, nobjs)); } }