在讲解Memcached的内存管理机制之前,我们先来看一下我们传统的内存管理机制是什么样子的?
【传统的内存管理机制】
在以前,内存的管理是通过对所以记录简单进行Malloc和free来进行的。也就是说,我们的传统内存分配使用malloc函数来对操作系统进行内存空间的申请,当内存不再使用的时候,通过free方法进行释放,再还给操作系统。
它的内存管理方式如下图:
这样的内存分配方式的缺点是:
1、每次内存申请都要与操作系统打交道,增加了操作系统的工作
2、很容易产生内存碎片。
如图fragment就是产生的内存碎片,导致内存分配的效率低下。因为当内存使用完毕进行释放,再将这个内存块分配给其他对象的时候,往往是需要的内存大于这些不规整的内存块,如果必须要占用这么多内存的话,它就要遍历所有没有使用的内存,直到找到可以分配的内存为止,这样就降低了内存分配的速度。
而我们的memcached采用的是预先分配和分组的方式进行内存分配管理,有效的缓解了内存碎片的问题,但没有完全解决。
【Memcached内存管理机制】
Memcached的对内存的管理是以slab为单元进行管理的。它是一次申请内存的最小单位。每个slab class对应着一个或者多个chunk。chunk是存放元素的最小单元。
memcached会根据元素的大小,将其放到合适的slab class中,每个slab class 中chunk的大小是一样的。所以元素放到chunk内存空间后,可能会有部分剩余空间。它是根据最优选择的。
slab,是厚片的意思。而chunk是厚厚的一块的意思。
memcached的内存管理可以用下图来表示:
每个slab class 可存储的元素总大小默认为1M。每个slab class中的chunk的大小是一致的。但是,不同的slab class中的chunk是不同的。每次申请内存的时候,都会去寻找最合适的slab class组来存储。例如,我有一个 108 bytes的对象,它会优先选择slab class 2来进行存储。这种方式有效的防止了内存的碎片化。
但是如果,我有一个100bytes的对象,本来应该存储到112bytes的slab class 2 中,但是slab class 2中的内存都占用了怎么办?它会使用slab class 3来进行存储吗?
答案是否定的,它不会存储到下一个slab class中,而是采用过期删除机制LRU(Least Recently Used 近期最少使用算法)将对象继续存储到slab class2 中,它会替换过期的数据。但当数据失效了之后,并没有删除,total items还有,并没有从内存删除,但是curr_items里面已经没有了。
如下图:total item为3,而curr_items已经为0。这个过期只是让用户看不到数据,并没有在过期时在内存中立即删除,这称为惰性失效 ,节省了CPU时间和检测的成本。
【实践】
我们可以根据命令行来进行操作,看一下我们memcached服务器中的slab class 是多少个,每个chunk的大小是多少。
每个slab中chunk大小和下一个slab 中chunk大小的比例默认为1.5,这个比例的名字叫做增长因子。但是我们可以根据网站的需求,对增长因子进行修改,例如我把增长因子改为2.0,这样我们就可以有效的利用内存空间了。
【总结】
以上简单介绍了Memcached的内存管理机制,学习的时候,想起了Mongo中的chunk size,mongo这块内存管理没有研究过,估计也是类似的吧。最近学习的这些东西,感觉都能够和操作系统搭上边,觉得学习起来也挺简单的,就比如说之前看Java,没弄懂那个内存机制,现在再看马士兵的视频,也觉得这个东西挺简单的。