犹记得当年学习C++的时候看STL的源码,侯捷的一个观点让我深以为然,在他的源码剖析中,空间配置器是放在最开头讲,很多人都不注意内存管理部分,而内存管理模块一般都不会耦合其他模块,所以即使你一直不看内存管理模块的实现也不影响对其他模块的研究,但你却会发现内存管理模块绝对是贯穿所有的。

不过Redis并不是一个函数库,因此它没必要像STL的空间配置器一样去做细致的管理,如二级空间配置器构造free_list+memory_poll去管理小块内存的使用与释放(这种实现其实被用于很多地方,malloc实现本身也用了类似的链表、Mysql的innodb引擎底层的内存管理也是如此),一级空间配置器直接找malloc要内存。这样细致的管理在Redis中不需要,因为Redis的内存管理只用于自己内部,STL空间配置器是用于提供给STL中每个容器,而每个容器是直接被用户使用,这与Redis要做的内存管理是有很大区别的,这也是Redis为什么没有自己去搞一套细致的内存管理的原因,原因就是不太需要,Redis作者可以管理好内存,其他用STL容器的程序员就未必了。


Redis中的内存管理库叫zmalloc,为什么z开头呢,因为在前面加一个z可以与其它malloc一眼区分开,并且字母z开头的单词较少不会引起歧义(以上真正元原因当然只有作者本人知道)。zmalloc封装了三种malloc,一下于zmalloc.h:

image.png
1553931003837726.png

简单介绍上面的这三种malloc,以后再详细对比一下这三种malloc

malloc: 这是平时linux系统中自带的malloc,又称为ptmalloc,性能很一般

tcmalloc: 由google开发,性能要比malloc快很多,在多线程下表现尤为明显,例如chrome中就是用的自家tcmalloc

jemalloc: facebook开发,性能接近tcmalloc,同样在多线程环境下非常优秀

此处注意HAVE_MALLOC_SIZE以及**zmalloc_size(p)**这两个宏,这两个宏在后续很多地方会出现,当HAVE_MALLOC_SIZE这个宏被定义时表明:当前使用的malloc库函数提供了显示某段内存大小的函数。这样 表达好像是有点绕的感觉(实在是没有更好的表达了),举个例子,我们通过某个malloc函数开辟指定大小的内存,获取了这段内存的指针p,我们可以通过这个malloc库函数提供的某个函数直接可以获取这段内存的小,在这里我例如jemalloc库这个函数为je_malloc_usable_size(p),如下

image.png
1553932817532721.png

我们看到tcmalloc、jemalloc、ptmalloc中都提供了这样一个函数,同时获取某个指针后面管理的内存这个功能几乎是必须的,考虑到可扩展性,我们仍然应该考虑当某些malloc库本身不提供该函数的情况,后面我们可以看到zmalloc自己也确实封装了一个这样的实现,实现方法就是在开辟空间的头部用一定大小存储这段被管理内存的大小,后面我们会看到。同时,实现这个功能的函数都被定义成宏zmalloc_size(p)。


函数原型声明

image.png
1553933076913373.png

可以看到,如果没有定义HAVE_MALLOC_SIZE,我们就会自己定义一个zmalloc_size函数,实现获取这段被管理内存大小的功能。事实上,这个函数并没有被用到……