技术开发 频道

Redis源码分析:内存分配操作

  【IT168技术】Redis中到处都会进行内存分配操作。为了屏蔽不同平台之间的差异,以及统计内存占用量等,Redis对内存分配函数进行了一层封装,程序中统一使用zmalloc,zfree一系列函数,位于zmalloc.h,zmalloc.c文中。

  上边说过,封装就是为了屏蔽底层平台的差异,同时方便自己实现相关的统计函数。具体来说就是:

  ①若系统中存在Google的TC_MALLOC库,则使用tc_malloc一族函数代替原本的malloc一族函数。

  ②若当前系统是Mac系统,则使用中的内存分配函数。

  ③其他情况,在每一段分配好的空间前头,同时多分配一个定长的字段,用来记录分配的空间大小。

  源代码分别在 config.h 和 zmalloc.c 中:

/* config.h */
#
if defined(USE_TCMALLOC)
#include
<google/tcmalloc.h>
#
if TC_VERSION_MAJOR >= 1 && TC_VERSION_MINOR >= 6
#define HAVE_MALLOC_SIZE
1
#define redis_malloc_size(p) tc_malloc_size(p)
#endif
#elif defined(__APPLE__)
#include
<malloc/malloc.h>
#define HAVE_MALLOC_SIZE
1
#define redis_malloc_size(p) malloc_size(p)
#endif
 
/* zmalloc.c */
#ifdef HAVE_MALLOC_SIZE
#define PREFIX_SIZE (
0)      
#
else  
#
if defined(__sun)
#define PREFIX_SIZE (sizeof(
long long))
#
else                        
#define PREFIX_SIZE (sizeof(size_t))
#endif
#endif

  因为 tc_malloc 和 Mac平台下的 malloc 函数族提供了计算已分配空间大小的函数(分别是tc_malloc_size和malloc_size),所以就不需要单独分配一段空间记录大小了。而针对linux和sun平台则要记录分配空间大小。对于linux,使用sizeof(size_t)定长字段记录;对于sun os,使用sizeof(long long)定长字段记录。也就是上边源码中的 PREFIX_SIZE 宏。

  那么这个记录有什么用呢?答案是,为了统计当前进程到底占用了多少内存。在 zmalloc.c 中,有这样一个静态变量:

static size_t used_memory = 0;

   它记录了进程当前占用的内存总数。每当要分配内存或是释放内存的时候,都要更新这个变量。因为分配内存的时候,可以明确知道要分配多少内存。但是释放内存的时候,(对于未提供malloc_size函数的平台)仅通过指向要释放内存的指针是不能知道释放的空间到底有多大的。这个时候,上边提到的PREFIX_SIZE定长字段就起作用了,可以通过其中记录的内容得到空间的大小。zmalloc函数如下(去掉无关代码):

void *zmalloc(size_t size) {
    void
*ptr = malloc(size+PREFIX_SIZE);

    
if (!ptr) zmalloc_oom(size);
    
*((size_t*)ptr) = size;
    update_zmalloc_stat_alloc(size
+PREFIX_SIZE,size);
    
return (char*)ptr+PREFIX_SIZE;
#endif
}

   看到在分配空间的时候,空间大小是size+PREFIX_SIZE。对于mac系统或是使用tc_malloc的情况,PREFIX_SIZE 为0。之后将ptr指针指向的空间前size_t中记录分配空间的大小。最后返回的是越过记录区的指针。zfree函数类似(去掉无关代码):

void zfree(void *ptr) {
    void
*realptr;
    size_t oldsize;

    
if (ptr == NULL) return;
    realptr
= (char*)ptr-PREFIX_SIZE;
    oldsize
= *((size_t*)realptr);
    update_zmalloc_stat_free(oldsize
+PREFIX_SIZE);
    free(realptr);
#endif
}

   先将指针向前移动PREFIX_SIZE,然后取出分配空间时保存的空间长度。最后free整个空间。

0
相关文章