本文基于memcached 1.2.0写成
memcached的内存分配器slab.c不过300行代码,还是比较容易上手分析的。
内存模型如下:
一个slabclass_t管理了多个slab,每个slab被称为内存页,每个slab管理多个item的内存空间核心函数
函数名 | 作用 |
---|---|
slabs_init | 初始化slabclass_t结构体数组 |
slabs_clsid | 通过内存大小从slabclass_t数组中找到最小能满足的结构体 |
slabs_preallocate | 给每个slabclass_t先分配一个slab(页)的内存(1mb) |
slabs_newslab | 给指定的slabclass分配一个新的slab存放到slab_list上,同时slabs、end_page_ptr、end_page_free发生相应变化 |
grow_slab_list | 动态增加slab_list数组的大小 |
slabs_alloc | 从内存分配器中取出一个空的item内存来使用 |
slabs_free | 将item所在的内存指针重新标记成可使用,相当于删除了item |
slabs_stats | 从slabclass_t结构体上获取内存分配器的使用情况 |
其中被slab.c以外的文件调用的函数有slabs_alloc、slabs_free、slabs_init和slabs_stats
slabs_init内部调用关系
slabs_alloc内部调用关系
内存分配生命周期
初始化机制-slabs_init()函数
- 计算每个item的初始占用的内存大小
unsigned int size = sizeof(item) + settings.chunk_size;
复制代码
- 根据size值和每个内存页1mb大小算出当前slabclass_t的每个slab能够存放多少个item放到perslab属性上
- 按照factor这个缩放值增大size(size=size*factor)继续初始化下一个slabclass_t
获取item的内存机制-slabs_alloc()函数
这里有一个分支,如果当前slabclass的当前slab还有剩余的内存空间,直接就分配了,参考:
if (! (p->end_page_ptr || p->sl_curr || slabs_newslab(id)))
return 0;
复制代码
三种情况
- 当前页还有内存空间 p->end_page_ptr
- 有item被删除了释放的空间p->sl_curr
- 给当前的slabclass_t分配一个新页 slabs_newslab(id)
/* return off our freelist, if we have one */
if (p->sl_curr)
return p->slots[--p->sl_curr];
/* if we recently allocated a whole page, return from that */
if (p->end_page_ptr) {
void *ptr = p->end_page_ptr;
if (--p->end_page_free) {
p->end_page_ptr += p->size;
} else {
p->end_page_ptr = 0;
}
return ptr;
}
复制代码
删除item的内存释放机制
主要就是把删除的item放进slabclass_t的slots数组中,申请内存时,优先从这个slots中获取,达到这个memcached解决内存碎片的目的
这里有个对这个存放被删除的item的slots数组扩容的操作
if (p->sl_curr == p->sl_total) { /* need more space on the free list */
请到「今天看啥」查看全文