一级配置器
class __malloc_alloc
{
using malloc_handler = void(*)(); // 函数指针
private:
// 用于处理内存不足的情况
static void *oom_malloc(size_t);
static void *oom_realloc(void* , size_t);
// 处理函数
static malloc_handler __malloc_alloc_oom_handler;
public:
static void *allocate(size_t n)
{
// 分配器
void *result = malloc(n);
if (result == nullptr)
{
// 申请失败,处理内存不足的情况,使用oom_malloc函数再次申请
oom_malloc(n);
}
return result;
}
static void* deallocate(void* p, size_t n)
{
free(p);
}
static void* reallocate(void * p, size_t, size_t new_size)
{
void* result = realloc(p, new_size); // 内置的 在p的位置申请size大小
if (result == nullptr)
{
// 空间不足,使用oom_realloc重新分配
oom_realloc(p, new_size);
}
}
};
// 函数指针指向空,不处理
typename __malloc_alloc::malloc_handler
__malloc_alloc::__malloc_alloc_oom_handler = nullptr;
// allocate 申请失败时的处理函数 oom_malloc函数的实现
void * __malloc_alloc::oom_malloc(size_t n)
{
// 再次申请
malloc_handler new_alloc_handler;
void * result;
for(;;)
{
// 循环尝试?
new_alloc_handler = __malloc_alloc_oom_handler;
if (new_alloc_handler == nullptr)
{
throw std::bad_alloc();
}
(*new_alloc_handler)(); // 调用函数
result = malloc(n);
if (result != nullptr)
{
return result;
}
}
}
// realloc失败处理函数
void * __malloc_alloc::oom_realloc(void* p, size_t n)
{
malloc_handler new_alloc_handler;
void * res;
for (;;)
{
new_alloc_handler = __malloc_alloc_oom_handler;
if (new_alloc_handler == nullptr)
{
throw std::bad_alloc();
}
(*new_alloc_handler)();
res = realloc(p, n);
if (res != nullptr)
{
return res;
}
}
}
using malloc_alloc = __malloc_alloc; // 一级配置器
二级配置器
// freelist参数设定
enum __freelist_setting
{
__ALIGN = 8,
__MAX_BYTES = 128,
__NFREELISTS = __MAX_BYTES / __ALIGN
};
class __default_alloc
{
private:
// 将字节转换为8的倍数,向上(大)取 ---- 使用计算器尝试
// 7bytes
// 7 + 7 & ~7 14 & ~7 1110 & ~111 1110 &1000 = 8 即第一个链表
// 15bytes
// 15 + 7 & ~7 22 & ~7 10110 & 00000 = 16 及第二个链表
// 22bytes
// 22 + 7 = 29 & ~7 11101& 11000 = 11000 = 24
// 30bytes
// 37 & ~7 100101 & 111000 = 100000 = 32
static size_t ROUND_UP(size_t bytes)
{
return (((bytes) + static_cast<size_t>(__ALIGN) -1) &
~(static_cast<size_t>(__ALIGN) -1) );
}
private:
// free_list 节点
// 由于union特性 并不需要占用额外空间
union obj
{
union obj* free_list_link; // 指向下一个节点
char clinet_data[1]; // 指向资源
};
private:
static obj* volatile free_list[__NFREELISTS]; // vol 表示存放在内存中可变
static size_t FREELIST_INDEX(size_t bytes)
{
return (bytes + static_cast<size_t>(__ALIGN) - 1) /
static_cast<size_t>(__ALIGN) - 1;
// 3 bytes 3 + 7 = 10 / 8 - 1 = 0
// 13 bytees 13 + 7 = 20 / 8 - 1 = 1
// 23 BYTES 23 + 7 = 30 / 8 - 1 = 2
// 100 BYTES = 107 / 8 - 1 = 12
}
static void* refill(size_t n); // freelist没有可用空间时操作
static char* chunk_alloc(size_t size, int &nobjs);
static char* start_free; // 内存池的开始位置
static char* end_free; // 内存池的结束位置
static size_t heap_size;
public:
static void *allocate(size_t n); // 分配n大小的空间
static void deallocate(void *p , size_t n); // 释放p处n大小的空间
static void* reallocate(void * p, size_t old_sz, size_t new_sz); // 将p处old大小释放,申请new大小
};
实现
char* __default_alloc::start_free = nullptr; // 内存池初始
char* __default_alloc::end_free = nullptr;
size_t __default_alloc::heap_size = 0;
__default_alloc::obj * volatile __default_alloc::free_list[__NFREELISTS] = {
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,nullptr
};
// freelist无可用区块的时候, 重新填充空间,
// 新空间取自内存池, 默认获取20个节点
// 若内存池不足,则获取的将小于20 获取20 * n个大小
void * __default_alloc::refill(size_t n)
{
// 这里的n就是8 16 24 32 。。。 各个区块的大小
int nobjs = 20; // obj区块
char * chunk = chunk_alloc(n, nobjs); // chunk_alloc() 区块分配 n默认是8的倍数, nobjs使用引用接受,需要修改
obj* volatile *my_free_list;
obj* result;
obj* current_obj, *next_obj;
// 只能搞到一块这么大的 直接返回
if (nobjs == 1)
{
return (chunk);
}
// 不止一块
my_free_list = free_list + FREELIST_INDEX(n); // 回到该链表的头结点处
result = reinterpret_cast<obj*>(chunk); // 将申请到的结果转化为块
// 下述操作是将这些块连接起来
*my_free_list = next_obj = reinterpret_cast<obj*>(chunk + n); // 取走n之后指针所指
for (int i = 1;; i++)
{
// 循环,模拟不断申请
current_obj = next_obj;
next_obj =
reinterpret_cast<obj*>(reinterpret_cast<char*>(next_obj) + n); // 一块
if (nobjs - 1 == i)
{
// 取走20块,
current_obj->free_list_link = nullptr;
break;
}
else
{
// 使用指针串连上
current_obj->free_list_link = next_obj;
}
}
return result;
}
char* __default_alloc::chunk_alloc(size_t size, int& nobjs)
{
// 开辟nobjs个 size(8 16 24...) 大小的空间
char* result;
size_t total_bytes = size * nobjs;
size_t bytes_left = end_free - start_free; // 内存池剩余空间
if (bytes_left >= total_bytes)
{
// 内存池足够支撑分配,直接从内存池获取
result = start_free;
start_free += total_bytes;
return result;
}
else if (bytes_left > size) // 我的想法的>=
{
// 内存池容量不够20 个, 但是足够一个以上,将这些块全部获取了,更新内存池头尾
nobjs = static_cast<int>(bytes_left / size); // 内存池中的该块实际个数
total_bytes = size * nobjs;
result = start_free;
start_free = start_free + total_bytes;
return result;
}
else
{
// 内存池仅有小于一个的大小
// 申请空间一般大小是size*nobjs * 2 + random
size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);
// heapsize会随着申请的次数逐渐增加
if (bytes_left > 0)
{
// 内存池还有,但是不够1块,不要浪费了,
obj* volatile *my_free_list = free_list + FREELIST_INDEX(bytes_left); // 定位到这块开头
reinterpret_cast<obj*>(start_free)->free_list_link = *my_free_list; // 从不浪费的那里作为内存池的开头
*my_free_list = reinterpret_cast<obj*>(start_free); // 以块的形式
}
start_free = reinterpret_cast<char*>(malloc(bytes_to_get));
if (start_free == nullptr)
{
// 堆空间不足以分配
obj* volatile *my_free_list;
obj* p;
// 检查其他拉链中有没有<=128bytes的区块
for (size_t i = 0; i < static_cast<size_t>(__MAX_BYTES); i += static_cast<size_t>(__ALIGN))
{
my_free_list = free_list + FREELIST_INDEX(i); // 遍历每一个头
p = *my_free_list;
if (p != nullptr)
{
// 有拉链 将拉链中的空间提取出来
*my_free_list = p->free_list_link;
start_free = reinterpret_cast<char*>(p);
end_free = start_free + i;
return (chunk_alloc(size, nobjs)); // 递归调用,修正nobjs 此时必然进入else if 分支
}
}
end_free = nullptr; // 到处都找不到内存
// 调用一级配置器
start_free = reinterpret_cast<char*>(malloc_alloc::allocate(bytes_to_get));
}
// 分配了堆空间,大小够不够递归跑一下
heap_size += bytes_to_get; // 已占用堆的内存大小
end_free = start_free + bytes_to_get;
return chunk_alloc(size, nobjs); // 调用自身修正nobjs
}
}
void * __default_alloc::allocate(size_t n)
{
obj * volatile * myfreelist;
obj * result;
if (n > __MAX_BYTES)
{
// 大于128字节 使用一级配置器
return (malloc_alloc::allocate(n));
}
// 二级配置器
// 链表是类似hash的拉链法,将其他20个节点以链表的形式 挂到每个节点上
myfreelist = free_list + FREELIST_INDEX(n); // 链表首地址 + 下标
// myfreelist就是记录大hash表中,拉链的头节点地址
result = *myfreelist;
if (result == nullptr)
{
// 说明该拉链没有空间,需要填充
void * r = refill(ROUND_UP(n)); // 分配n 比如我要34bytes 那么是从40bytes为一块这里进行填充,填充20个
return r;
}
// 该处不需要填充,直接从里面取一块空间
*myfreelist = result->free_list_link; // 指向这一块的链表区
return result;
}
// 回收p
void __default_alloc::deallocate(void* p, size_t n)
{
if (n > static_cast<size_t>(__MAX_BYTES))
{
malloc_alloc::deallocate(p, n); // 如果要申请的n过大,使用一级配置器
}
else
{
obj* volatile *my_free_list = free_list + FREELIST_INDEX(n);
obj* q = reinterpret_cast<obj*>(p);
q->free_list_link = *my_free_list; // 指向头 就回收了
*my_free_list = q;
}
}
void *__default_alloc::reallocate(void* p, size_t old_sz, size_t new_sz)
{
void* result ;
size_t copy_sz;
// 都是128以上的,使用一级配置
if (old_sz > static_cast<size_t>(__MAX_BYTES)
&& new_sz > static_cast<size_t>(__MAX_BYTES))
{
return realloc(p, new_sz);
}
// 如果这两个的区块大小相同,即在同一个拉链上
if (ROUND_UP(old_sz) == ROUND_UP(new_sz))
{
return p;
}
result = allocate(new_sz); // 配置newsize大小
copy_sz = new_sz > old_sz ? old_sz : new_sz; // 小的
memcpy(result, p, copy_sz); // 原来的都拷贝过来,多了的不要
deallocate(p, old_sz); // 二级配置器的p位置,回收oldsz大小
return result;
}