Redis是一种使用C语言编写的NoSQL数据库,特点是高性能,高灵活性。但由于C语言没有自带GC,所以Redis在实际使用过程中,内存的分配和释放的合理性和高效性就非常重要。为了达到这个目的,Redis的实现中封装了C里的malloc,calloc,realloc和free函数来对自己的内存进行管理。这些实现都在zmalloc.h和zmalloc.c中。程序中维护一个use_memory,表示已用内存的大小,每当分配或释放内存时都会对其进行更新。
void *zmalloc(size_t size) { void *ptr = malloc(size+PREFIX_SIZE); if (!ptr) zmalloc_oom_handler(size); #ifdef HAVE_MALLOC_SIZE update_zmalloc_stat_alloc(zmalloc_size(ptr)); return ptr; #else *((size_t*)ptr) = size; update_zmalloc_stat_alloc(size+PREFIX_SIZE); return (char*)ptr+PREFIX_SIZE; #endif }可以看到,zmalloc函数传入一个size变量,表示当前想要分配的内存空间。然后调用封装起来的malloc分配了size+PREFIX_SIZE个字节。其中PREFIX_SIZE的宏定义如下:
#ifdef HAVE_MALLOC_SIZE #define PREFIX_SIZE (0) #else #if defined(__sun) || defined(__sparc) || defined(__sparc__) #define PREFIX_SIZE (sizeof(long long)) #else #define PREFIX_SIZE (sizeof(size_t)) #endif #endif对于不同平台来说,PREFIX_SIZE的值是不同的
*((size_t*)ptr) = size;ptr的前size_t个字节空间存放的是size,也就是本次分配空间的大小。
而update_zmalloc_stat_alloc是一个宏(不写成函数的原因可能是因为效率问题)。它的目的是更新used_memory,其定义如下:
#define update_zmalloc_stat_alloc(__n) do { \ size_t _n = (__n); \ if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \ if (zmalloc_thread_safe) { \ update_zmalloc_stat_add(_n); \ } else { \ used_memory += _n; \ } \ } while(0)理解这句话是很关键的:
if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1));说白了它进行了一个内存对齐的过程。因为在操作系统申请内存的时候,往往是对齐的,简单来讲,这样方便数据存放与传输。具体内容可以搜索“内存对齐”。
关于zmalloc和它封装的malloc的关系,可以用下图来说明: