Memcached
作为内存cache
服务器,内存高效管理是其最重要的任务之一,Memcached使用SLAB
管理其内存,SLAB内存管理直观的解释就是分配一块大的内存,之后按不同的块(48byte, 64byte, … 1M)等切分这些内存,存储业务数据时,按需选择合适的内存空间存储数据。
Memcached首次默认分配64M的内存,之后所有的数据都是在这64M空间进行存储,在Memcached启动之后,不会对这些内存执行释放操作,这些内存只有到Memcached进程退出之后会被系统回收,下面分析下Memcached的内存初始化过程。
//内存初始化,settings.maxbytes是Memcached初始启动参数指定的内存值大小,settings.factor是内存增长因子
slabs_init(settings.maxbytes, settings.factor, preallocate);
#define POWER_SMALLEST 1 //最小slab编号
#define POWER_LARGEST 200 //首次初始化200个slab
//实现内存池管理相关的静态全局变量
static size_t mem_limit = 0;//总的内存大小
static size_t mem_malloced = 0;//初始化内存的大小,这个貌似没什么用
static void *mem_base = NULL;//指向总的内存的首地址
static void *mem_current = NULL;//当前分配到的内存地址
static size_t mem_avail = 0;//当前可用的内存大小
static slabclass_t slabclass[MAX_NUMBER_OF_SLAB_CLASSES];//定义slab结合,总共200个
void slabs_init(const size_t limit, const double factor, const bool prealloc) {
int i = POWER_SMALLEST - 1;
//size表示申请空间的大小,其值由配置的chunk_size和单个item的大小来指定
unsigned int size = sizeof(item) + settings.chunk_size;
mem_limit = limit;//mem_limit是全局变量
if (prealloc) {
//支持预分配
mem_base = malloc(mem_limit);//申请地址,mem_base指向申请的地址
if (mem_base != NULL) {
//mem_current指向当前地址
mem_current = mem_base;
//可用内存大小为mem_limit
mem_avail = mem_limit;
} else {
//支持预分配失败
fprintf(stderr, "Warning: Failed to allocate requested memory in"
" one large chunk.\nWill allocate in smaller chunks\n");
}
}
//置空slabclass数组
memset(slabclass, 0, sizeof(slabclass));
//开始分配,i<200 && 单个chunk的size<单个item最大大小/内存增长因子
while (++i < POWER_LARGEST && size <= settings.item_size_max / factor) {
//size执行8byte对齐
if (size % CHUNK_ALIGN_BYTES)
size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES);
slabclass[i].size = size;
//slab对应chunk的大小
slabclass[i].perslab = settings.item_size_max / slabclass[i].size;
//slab对应的chunk的个数
size *= factor;
//size下一个值为按增长因子的倍数增长
if (settings.verbose > 1) {
//如果有打开调试信息,则输出调试信息
fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n",
i, slabclass[i].size, slabclass[i].perslab);
}
}
//循环结束时,size已经增长到1M
power_largest = i;//再增加一个slab
slabclass[power_largest].size = settings.item_size_max;
//slab的size为item_size_max
slabclass[power_largest].perslab = 1;//chunk个数为1
//打印调试信息
if (settings.verbose > 1) {
fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n",
i, slabclass[i].size, slabclass[i].perslab);
}
//读取环境变量T_MEMD_INITIAL_MALLOC的值
{
char *t_initial_malloc = getenv("T_MEMD_INITIAL_MALLOC");
if (t_initial_malloc) {
mem_malloced = (size_t)atol(t_initial_malloc);
}
}
if (prealloc) {
//分配每个slab的内存空间,传入最大已经初始化的最大slab编号
slabs_preallocate(power_largest);
}
}
//分配每个slab的内存空间
static void slabs_preallocate (const unsigned int maxslabs) {
int i;
unsigned int prealloc = 0;
for (i = POWER_SMALLEST; i <= POWER_LARGEST; i++) {
if (++prealloc > maxslabs)
return;
//执行分配操作,对第i个slabclass执行分配操作
if (do_slabs_newslab(i) == 0) {
fprintf(stderr, "Error while preallocating slab memory!\n"
"If using -L or other prealloc options, max memory must be "
"at least %d megabytes.\n", power_largest);
exit(1);
}
}
}
//执行分配操作
static int do_slabs_newslab(const unsigned int id) {
slabclass_t *p = &slabclass[id];//p指向第i个slabclass
int len = settings.slab_reassign ? settings.item_size_max:p->size*p->perslab;
char *ptr;
//grow_slab_list初始化slabclass的slab_list,而slab_list中的指针指向每个slab
//memory_allocate从内存池申请1M的空间
if ((mem_limit && mem_malloced + len > mem_limit && p->slabs > 0) ||
(grow_slab_list(id) == 0) ||
((ptr = memory_allocate((size_t)len)) == 0)) {
MEMCACHED_SLABS_SLABCLASS_ALLOCATE_FAILED(id);
return 0;
}
memset(ptr, 0, (size_t)len);
//将申请的1M空间按slabclass的size进行切分
split_slab_page_into_freelist(ptr, id);
p->slab_list[p->slabs++] = ptr;//循环分配
mem_malloced += len;//增加已经分配出去的内存数
MEMCACHED_SLABS_SLABCLASS_ALLOCATE(id);
return 1;
}
//初始化slabclass的slab_class,而slab_list中的指针指向每个slab,id为slabclass的序号
static int grow_slab_list (const unsigned int id) {
slabclass_t *p = &slabclass[id];
//p指向第id个slabclass;
if (p->slabs == p->list_size) {
size_t new_size = (p->list_size != 0) ? p->list_size * 2 : 16;//new_size如果是首次分配,则取16,否则按旧值的2倍扩容
void *new_list = realloc(p->slab_list, new_size * sizeof(void *));//申请空间,这个空间是从系统分配,不是从内存池分配
if (new_list == 0) return 0;
p->list_size = new_size;//修改第id个slabclass的值
p->slab_list = new_list;
}
return 1;
}
//从内存池分配size个空间
static void *memory_allocate(size_t size) {
void *ret;
if (mem_base == NULL) {//如果内存池没创建,则从系统分配
ret = malloc(size);
} else {
ret = mem_current;
//size大于剩余的空间
if (size > mem_avail) {
return NULL;
}
//按8字节对齐
if (size % CHUNK_ALIGN_BYTES) {
size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES);
}
//扣除size个空间
mem_current = ((char*)mem_current) + size;
if (size < mem_avail) {
mem_avail -= size;//更新剩余空间大小
} else {
mem_avail = 0;
}
}
return ret;
}
//将ptr指向的内存空间按第id个slabclass的size进行切分
static void split_slab_page_into_freelist(char *ptr, const unsigned int id) {
slabclass_t *p = &slabclass[id];
int x;
//每个slabclass有多个slab,对每个slab按slabclass对应的size进行切分
for (x = 0; x < p->perslab; x++) {
do_slabs_free(ptr, 0, id);//创建空闲item
ptr += p->size;
}
}
//创建空闲item
static void do_slabs_free(void *ptr, const size_t size, unsigned int id)
{
slabclass_t *p;
item *it;
assert(((item *)ptr)->slabs_clsid == 0);
assert(id >= POWER_SMALLEST && id <= power_largest);//判断id有效性
if (id < POWER_SMALLEST || id > power_largest)
return;
MEMCACHED_SLABS_FREE(size, id, ptr);
p = &slabclass[id];
it = (item *)ptr;
it->it_flags |= ITEM_SLABBED;
it->prev = 0;
it->next = p->slots;//挂载到slabclass的空闲链表中
if (it->next) it->next->prev = it;
p->slots = it;
p->sl_curr++;//空闲item个数+1
p->requested -= size;//已经申请到的空间数量更新
return;
}