内存池:
为了节省申请小块内存与释放小块内存时的开销,可以使用内存池,开始时根据需要申请一片较大的内存,在申请小块内存的时候从内存池中获取,在释放小块内存时,将内存放回内存池
内存池中的内存块:
块的大小可以由内存池的编写者确定,一般为8的倍数:8,16,24,32,.....,128;单位为字节,相同大小的块通过指针串成一串(和单链表一样)
内存池的两个部分:
1.未区块化的内存:这是一片连续的内存,可以通过两个指针 begin和end 表示它
2.区块化的内存:区块化的内存通过链表的形式保存着
在申请小块内存的时候,是从区块内存链中取,而不是从未区块化的内存中取
内存池中的区块化:
将大片的内存转换成许多内存块,如:将80个字节连续的内存,转换成10个大小为8字节的内存,同时需要将这10个内存块像链表一样串起来
为什么需要块?
因为如果没有块,在回收内存的时候,回收的内存可能与内存池中未区块化的内存是不连着的,所有没办法通过简单的两个指针来表示他们。
实现:
#include <cstddef>
#include <cstdlib>
#include <climits>
#include <iostream>
#define ALIGN 8 //最小的内存块及内存块增加的梯度
#define MAX_BYTES 128 //最大的内存块
template<int inst>
class memory_pool{
private:
union block{
union block* next; //指向下一个区块
char d[1];
};
static block * volatile free_list[MAX_BYTES/ALIGN]; //数组元素指向区块化的链表
static char *begin; //未区块化的内存池的头
static char *end; //未区块化的内存池的尾
static size_t heap_number; //内存池向堆申请内存的次数
private:
//将n上升到ALIGN的倍数
static size_t ALIGN_BYTES(size_t n){
return ( (n+ALIGN - 1)/ALIGN) * ALIGN;
}
//根据n的大小,获取free_list的下标
static size_t FREE_LIST_INDEX(size_t n){
return (n - 1)/ALIGN;
}
//从未区块化的内存中获取内存,或者向堆申请
static void *getMemory(size_t bytes, size_t& blocks){
size_t bytes_remain = end - begin; //现在还拥有的未区块化的内存,单位为字节
size_t bytes_need = bytes * blocks; //需要获取的内存,单位为字节
void *result;
//(1).可申请到所有需要的区块
if(bytes_remain >= bytes_need){
result = begin;
begin += bytes_need;
return result;
}
//无法满足(1),但是可以申请1个或以上的区块
if(bytes_remain >= bytes){
blocks = bytes_remain / bytes; //可以获取的块数
result = begin;
begin += blocks * bytes; //调整为区块化的内存的范围
return result;
}
if(bytes_remain > 0){
//连1个区块都无法申请,则将剩余的未区块化的内存区块化
block * volatile * free_list_now = free_list + FREE_LIST_INDEX(bytes_remain);
block* new_block = (block *)begin; //剩余的内存区块化
block* old_block = *free_list_now; //链表拼接
new_block->next = old_block; //链表拼接
*free_list_now = new_block; //链表拼接
begin += bytes_remain; //调整为区块化的内存的范围,该语句执行后表示已无未区块化内存
}
//从堆中申请内存
void * alloc_