Memcache源码阅读(4)---内存管理
来源:互联网 发布:淘宝买家诈骗怎么办 编辑:程序博客网 时间:2024/05/21 07:10
我看的源码版本是1.2.4
memcached的存储
memcached拥有一个内存池,内存池中的内存分成多种大小的chunk,chunk的大小有一个基础大小(最小的chunk大小,base chunk size),然后后面大小的是以settings.factor
为因子不断的增大。比如说base chunk size为64byte, setting.factor = 1.5
,那么memcached拥有的chunk size有64byte
, 64 * 1.5 = 96byte
, 96 * 1.5 = 144byte
, … 一直到页的大小的1/2。
需要存储数据的时候,从slab中找一个最小匹配的chunk来存储数据。比如说,我现在有个数据大小是112byte,那么memcached就会找一个最小的、能容下这个数据的chunk来存储这个数据(144byte)。
这样做虽然有内存浪费,但是好处是不需要管理内存碎片的问题。
slabclass_t的简图
注:这个图是简化版的slabclass,我将slab_list看为一个管理着chunk的数组,事实上,他只是管理着多个slab,slab再管理着chunk而已。
如上图,用户向slabclass请求内存,memcached先会到对应大小到的slabclass_t中查看slot是否有空闲chunk,如果有的话,就从空闲数组中分配;没有,则会从slab中分配一个没用过的。当用户释放slab分配的chunk时,则会将它放到slabclass_t的slot里。
slab的结构
slab的结构体
typedef struct { unsigned int size; /* sizes of items */ //就是chunk的大小 unsigned int perslab; /* how many items per slab */ //每个slab中有多少个chunk,它会管理着一个slab_list,slab_list里面有多个slab,每个slab管理着perslab个chunk void **slots; /* list of item ptrs */ //指向空闲数组,回收释放的chunk unsigned int sl_total; /* size of previous array */ //空闲数组的大小 unsigned int sl_curr; /* first free slot */ //指向第一个没有回收chunk的空闲数组。 void *end_page_ptr; /* pointer to next free item at end of page, or 0 */ unsigned int end_page_free; /* number of items remaining at end of last alloced page */ unsigned int slabs; /* how many slabs were allocated for this class */ void **slab_list; /* array of slab pointers */ unsigned int list_size; /* size of prev array */ unsigned int killing; /* index+1 of dying slab, or zero if none */} slabclass_t;
这里蓝色的表示已经被用户使用过的chunk,即使它被用户释放,它只会放到slot中,end_page_ptr并不会管理那些申请了的后来被用户释放的chunk,end_page_ptr指向第一个还没被用过的chunk。
slabs初始化
slabs_init(settings.maxbytes, settings.factor);
#define POWER_SMALLEST 1#define POWER_LARGEST 200#define POWER_BLOCK 1048576 //1 M#define CHUNK_ALIGN_BYTES (sizeof(void *))static slabclass_t slabclass[POWER_LARGEST + 1]; static size_t mem_limit = 0;static size_t mem_malloced = 0;static int power_largest;void slabs_init(const size_t limit, const double factor) { int i = POWER_SMALLEST - 1; unsigned int size = sizeof(item) + settings.chunk_size; //这里是对用户设定的大小加大一个item结构体的大小,因为存储数据的时候,用户数据是存在item结构体之后,item结构体记录着这个数据的一些信息。 /* Factor of 2.0 means use the default memcached behavior */ if (factor == 2.0 && size < 128) size = 128; mem_limit = limit; memset(slabclass, 0, sizeof(slabclass)); while (++i < POWER_LARGEST && size <= POWER_BLOCK / 2) { /* Make sure items are always n-byte aligned */ //进行字节对齐 if (size % CHUNK_ALIGN_BYTES) size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES); slabclass[i].size = size; slabclass[i].perslab = POWER_BLOCK / slabclass[i].size; size *= factor; } power_largest = i; slabclass[power_largest].size = POWER_BLOCK; slabclass[power_largest].perslab = 1;}
默认情况下这样就初始化完了,设置好了slabclass的信息,没有给slabs真正开辟存储空间。当用户向slabclass申请空间的时候,memcached发现slab没有可用空间再向系统申请空间。那我们看看初始化时到底是如何给slabs分配内存空间的,memcached发现slab没有可用空间时申请内存空间的方式跟这个也类似。
初始化时预先分配slabs
//为每个chunk size大小的slab_class分配空间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; do_slabs_newslab(i); }}//为第i个slab_class分配空间,分配失败的时候返回0,成功返回1static int do_slabs_newslab(const unsigned int id) { slabclass_t *p = &slabclass[id]; int len = p->size * p->perslab; char *ptr; if (mem_limit && mem_malloced + len > mem_limit && p->slabs > 0) return 0; //grow slab list返回0的时候是失败 if (grow_slab_list(id) == 0) return 0; ptr = malloc((size_t)len); if (ptr == 0) return 0; memset(ptr, 0, (size_t)len); //指向第一个没有分配的chunk,slab分配chunk是从slab_list里顺序分配的。 p->end_page_ptr = ptr; p->end_page_free = p->perslab; //为每个slab分配一个1M的page p->slab_list[p->slabs++] = ptr; mem_malloced += len; return 1;}//为slab_list添加一个slab,需要看看slab_list是否已满。//如果满了,就需要分配更大的slab_list来装下slabs。如果满了,重新分配空间失败返回0。static int grow_slab_list (const unsigned int id) { slabclass_t *p = &slabclass[id]; if (p->slabs == p->list_size) { size_t new_size = (p->list_size != 0) ? p->list_size * 2 : 16; void *new_list = realloc(p->slab_list, new_size * sizeof(void *)); if (new_list == 0) return 0; p->list_size = new_size; p->slab_list = new_list; } return 1;}
为用户从slab中找个合适大小的chunk
void *do_slabs_alloc(const size_t size) { slabclass_t *p; //找到最小的能装下size的slab unsigned int id = slabs_clsid(size); p = &slabclass[id]; //如果slot中又空闲的chunk,那就从slot中取一个 /* return off our freelist, if we have one */ if (p->sl_curr != 0) return p->slots[--p->sl_curr]; //否则我们就从slab_list中取一个 /* 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 != 0) { p->end_page_ptr += p->size; } else { p->end_page_ptr = 0; } return ptr; } return NULL; /* shouldn't ever get here */}
用户释放chunk
void do_slabs_free(void *ptr, const size_t size) { unsigned char id = slabs_clsid(size); slabclass_t *p; p = &slabclass[id]; //如果slot中已经装满了空闲chunk,那么就得给slot分配一个更大的数组,否则直接将空闲chunk放到slot中 if (p->sl_curr == p->sl_total) { /* need more space on the free list */ int new_size = (p->sl_total != 0) ? p->sl_total * 2 : 16; /* 16 is arbitrary */ void **new_slots = realloc(p->slots, new_size * sizeof(void *)); if (new_slots == 0) return; p->slots = new_slots; p->sl_total = new_size; } p->slots[p->sl_curr++] = ptr; return;}
- Memcache源码阅读(4)---内存管理
- Memcache源码阅读(5)---哈希表管理
- Memcached内存管理源码阅读
- memcache中内存管理源码剖析
- memcache中内存管理源码剖析
- memcache中内存管理源码剖析
- Memcache源码阅读(2)---命令行使用
- Memcache源码阅读(6)---数据存储
- Memcache源码阅读(8)---多线程
- Memcache源码阅读(1)---看源码的心得
- memcache如何管理内存
- memcache 内存管理
- Memcache内存管理
- memcache 内存管理
- memcache 内存管理
- memcache的内存管理
- 详解memcache内存管理
- Memcache-Java-Client-Release源码阅读(之一)
- submit汉化 亲测可用
- Ubuntu系统安装matlab教程
- caffe之小小的CNN网络跑起来
- usaco2.4.3 Cow Tours
- python3 实现 linux 命令 tree
- Memcache源码阅读(4)---内存管理
- hgfs目录搭建及共享文件方法
- 台式机蓝牙适配器搜不到设备怎么办
- 什么是自组织特征映射网络?
- java 多线程学习笔记1-概念,实现方式
- DDMS中File Explorer目录不显示
- Java书籍Top 10
- Future和FutureTask
- caffe源码阅读——Blob类