开发环境
Windows 10 专业版
开发工具
Microsoft Visual Studio Community 2017版本15.9.12
参考书籍
《STL 源码剖析》
分析
SGI STL包含了一级空间配置器和二级空间配置器,其中一级空间配置器allocator采用malloc和free来
管理内存,和C++标准库中提供的默认allocator是一样的;而二级空间配置器allocator采用了基于
freelist原理的内存池机制实现内存管理。
重要数据结构
union _Obj {
union _Obj* _M_free_list_link;
char _M_client_data[1]; /* The client sees this. */
};
enum { _ALIGN = 8 };//对齐方式
enum { _MAX_BYTES = 128 };//字节在128字节以下使用二级空间配置器分配回收内存
enum { _NFREELISTS = 16 }; // _MAX_BYTES/_ALIGN
//16 个 free-lists
static _Obj* _S_free_list[_NFREELISTS];
//表示内存池
static char* _S_start_free;
static char* _S_end_free;
static size_t _S_heap_size;
辅助函数
//将__bytes 上调至 8的倍数
static size_t _S_round_up(size_t __bytes);
//根据区块大小返回,决定使用第n号free-list.n从0计算
static size_t _S_freelist_index(size_t __bytes);
主要函数
//配置一大块空间,可容纳nobjs个大小为"size"的区块
static char *_S_chunk_alloc(size_t __size, int &__nobjs)
//返回相应块数,在需要的情况下除了分配给用户的块需要关联起来
static void* _S_refill(size_t __n)
用户使用的函数
static void* allocate(size_t __n)
static void deallocate(void* __p, size_t __n)
实现代码
#include <iostream>
using namespace std;
class Allocator
{
private:
union _Obj {
union _Obj* _M_free_list_link;
char _M_client_data[1]; /* The client sees this. */
};
enum { _ALIGN = 8 };//对齐方式
enum { _MAX_BYTES = 128 };//字节在128字节以下使用二级空间配置器分配回收内存
enum { _NFREELISTS = 16 }; // _MAX_BYTES/_ALIGN
//16 个 free-lists
static _Obj* _S_free_list[_NFREELISTS];
//表示内存池
static char* _S_start_free;
static char* _S_end_free;
static size_t _S_heap_size;
//将__bytes 上调至 8的倍数
static size_t
_S_round_up(size_t __bytes)
{
return (((__bytes)+(size_t)_ALIGN - 1) & ~((size_t)_ALIGN - 1));
}
//根据区块大小返回,决定使用第n号free-list.n从0计算
static size_t _S_freelist_index(size_t __bytes) {
return (((__bytes)+(size_t)_ALIGN - 1) / (size_t)_ALIGN - 1);
}
//
//配置一大块空间,可容纳nobjs个大小为"size"的区块
static char *_S_chunk_alloc(size_t __size, int &__nobjs)
{
char* __result;
size_t __total_bytes = __size * __nobjs;//本次约定申请的总空间大小
size_t __bytes_left = _S_end_free - _S_start_free;//内存池的剩余空间大小
//内存池的内存够用
if (__bytes_left >= __total_bytes) {
__result = _S_start_free;//本次申请的空间的首地址
_S_start_free += __total_bytes;//更新当前内存池
return(__result);
}
//只够分配大于等于一块但是不满足第一个if(就是不能按照约定分配)
else if (__bytes_left >= __size) {
__nobjs = (int)(__bytes_left / __size);//内存池剩余空间够多少块
__total_bytes = __size * __nobjs;//当前需要向内存池申请的空间
__result = _S_start_free;//分配
_S_start_free += __total_bytes;//更新内存池
return(__result);
}
//内存池可以分配的内存小于__size(一块都不能够成功分配)
else {
//当前要申请的空间大小=2*total_bytes(之后一半放入自由列表维护,一半放入内存池维护)
size_t __bytes_to_get =
2 * __total_bytes + _S_round_up(_S_heap_size >> 4);//附加的内存(为了效率,每次如果执行到这里都多分配一些块)
// Try to make use of the left-over piece.
//将内存池中剩余的内存还有利用价值
if (__bytes_left > 0) {
_Obj** __my_free_list =
_S_free_list + _S_freelist_index(__bytes_left);
((_Obj*)_S_start_free)->_M_free_list_link = *__my_free_list;
*__my_free_list = (_Obj*)_S_start_free;
}
//内存池首地址指向新的申请的空间
_S_start_free = (char*)malloc(__bytes_to_get);
//如果申请失败
if (nullptr == _S_start_free) {
size_t __i;
_Obj** __my_free_list;
_Obj* __p;
// Try to make do with what we have. That can't
// hurt. We do not try smaller requests, since that tends
// to result in disaster on multi-process machines.
for (__i = __size;
__i <= (size_t)_MAX_BYTES;
__i += (size_t)_ALIGN) {
__my_free_list = _S_free_list + _S_freelist_index(__i);
__p = *__my_free_list;
//freelist内尚有未用的区块
if (0 != __p) {
*__my_free_list = __p->_M_free_list_link;
_S_start_free = (char*)__p;
_S_end_free = _S_start_free + __i;
return(_S_chunk_alloc(__size, __nobjs));
// Any leftover piece will eventually make it to the
// right free list.
}
}
_S_end_free = nullptr; // In case of exception.
_S_start_free = (char*)malloc(__bytes_to_get);
// This should either throw an
// exception or remedy the situation. Thus we assume it
// succeeded.
}
//内存池总容量(修正内存池参数)
_S_heap_size += __bytes_to_get;
_S_end_free = _S_start_free + __bytes_to_get;
//递归调用,修正__nobjs
return(_S_chunk_alloc(__size, __nobjs));
}
}
//返回相应块数,在需要的情况下除了分配给用户的块需要关联起来
static void* _S_refill(size_t __n)
{
int __nobjs = 20;
char* __chunk = _S_chunk_alloc(__n, __nobjs);
_Obj** __my_free_list;
_Obj* __result;
_Obj* __current_obj;
_Obj* __next_obj;
int __i;
//如果只获得了一个区块,这个区块分配给用户,无新区块无需修正
if (1 == __nobjs) return(__chunk);
//找到在freelist中的位置
__my_free_list = _S_free_list + _S_freelist_index(__n);
//
/* Build free list in chunk */
__result = (_Obj*)__chunk;
*__my_free_list = __next_obj = (_Obj*)(__chunk + __n);//除了第一块都需要加入自由列表
//关联剩余区块
for (__i = 1; ; __i++) {
__current_obj = __next_obj;
__next_obj = (_Obj*)((char*)__next_obj + __n);
if (__nobjs - 1 == __i) {
__current_obj->_M_free_list_link = 0;
break;
}
else {
__current_obj->_M_free_list_link = __next_obj;
}
}
return(__result);
}
public:
//向空间配置器申请空间
static void* allocate(size_t __n)
{
void* __ret = nullptr;
if (__n > (size_t)_MAX_BYTES) {
__ret = malloc(__n);
}
else {
_Obj** __my_free_list
= _S_free_list + _S_freelist_index(__n);
// Acquire the lock here with a constructor call.
// This ensures that it is released in exit or during stack
// unwinding.
_Obj* __result = *__my_free_list;
if (__result == 0)
__ret = _S_refill(_S_round_up(__n));
else {
*__my_free_list = __result->_M_free_list_link;
__ret = __result;
}
}
return __ret;
}
//向空间配置器归还空间
static void deallocate(void* __p, size_t __n)
{
if (__n > (size_t)_MAX_BYTES)
free(__p);
else {
_Obj** __my_free_list
= _S_free_list + _S_freelist_index(__n);
_Obj* __q = (_Obj*)__p;
// acquire lock
__q->_M_free_list_link = *__my_free_list;
*__my_free_list = __q;
// lock is released here
}
}
};
//初始化静态成员变量
char *Allocator::_S_start_free = nullptr;
char *Allocator::_S_end_free = nullptr;
size_t Allocator::_S_heap_size = 0;
Allocator::_Obj* Allocator::_S_free_list[_NFREELISTS];
//类Allocator成员函数实现
int main()
{
Allocator allocator;
int *arr = (int*)allocator.allocate(sizeof(int) * 10);
for (int i = 0; i < 10; i++)
arr[i] = (rand() % 100 + 1);
allocator.deallocate(arr, 40);
return 0;
}