Redis中的内存管理:关于zmalloc

        Redis是一种使用C语言编写的NoSQL数据库,特点是高性能,高灵活性。但由于C语言没有自带GC,所以Redis在实际使用过程中,内存的分配和释放的合理性和高效性就非常重要。为了达到这个目的,Redis的实现中封装了C里的malloc,calloc,realloc和free函数来对自己的内存进行管理。这些实现都在zmalloc.h和zmalloc.c中。程序中维护一个use_memory,表示已用内存的大小,每当分配或释放内存时都会对其进行更新。

Redis中的zmalloc

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的值是不同的

  • 若系统中存在Google的TC_MALLOC库,则使用tc_malloc一族函数代替原本的malloc一族函数。
  • 若当前系统是Mac系统,则使用中的内存分配函数。
  • 其他情况,则分配一个size_t的大小。
简单来讲,可以把PREFIX_SIZE看作一个size_t的大小(一般是4字节)。它的主要作用是来记录本次分配的内存空间大小,如下:

*((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)); 
说白了它进行了一个内存对齐的过程。因为在操作系统申请内存的时候,往往是对齐的,简单来讲,这样方便数据存放与传输。具体内容可以搜索“内存对齐”。
上面这句话先验证_n是不是sizeof(long)的整数倍,如果不是的话就进行一个向上取整的过程,令_n是sizeof(long)的整数倍。

关于zmalloc和它封装的malloc的关系,可以用下图来说明:

Redis中的内存管理:关于zmalloc_第1张图片



你可能感兴趣的:(Redis源码分析)