本文共 4701 字,大约阅读时间需要 15 分钟。
Redis在编译时便会指定内存分配器;内存分配器可以是 libc 、jemalloc或者tcmalloc,默认是jemalloc。在Redis中内存分配主要靠 zmalloc 实现,在zmalloc.c文件实现,在这个文件内还存在其他相关的操作函数。
void *zmalloc(size_t size); // 申请内存void *zcalloc(size_t size); // 申请内存,并初始化为0void *zrealloc(void *ptr, size_t size); // 重申请内存void zfree(void *ptr); // 释放内存
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}#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)static size_t used_memory = 0;
通过malloc申请内存时,会多申请一个 PREFIX_SIZE 的大小。这里的 PREFIX_SIZE 就是用于保存内存长度的。内存申请后,将内存长度保存进内存的头部 PREFIX_SIZE 个字节的内存中。并通过update_zmalloc_stat_alloc 调整 use_memory 的记录。
if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1));
这行代码的作用是判断_n
是不是sizeof(long)
的整数倍,如果不是则将_n
加上差值,使其成为整数倍。本质上是为了内存对齐的操作,实现内存的精准计算。我们需要注意的是,这段宏定义的作用只是为了use_memory
能够精准计算内存对齐之后的内存长度,而malloc
出来的内存已经实现了内存对齐和指针对齐。
#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
分析zmalloc
函数发现存在HAVE_MALLOC_SIZE
。当定义了HAVE_MALLOC_SIZE
,PREFIX_SIZE
的长度就为0。那这个HAVE_MALLOC_SIZE
是什么来头?这个答案可以在zmalloc.h
找到:
#if defined(USE_TCMALLOC)#define ZMALLOC_LIB ("tcmalloc-" __xstr(TC_VERSION_MAJOR) "." __xstr(TC_VERSION_MINOR))#include#if (TC_VERSION_MAJOR == 1 && TC_VERSION_MINOR >= 6) || (TC_VERSION_MAJOR > 1)#define HAVE_MALLOC_SIZE 1#define zmalloc_size(p) tc_malloc_size(p)#else#error "Newer version of tcmalloc required"#endif#elif defined(USE_JEMALLOC)#define ZMALLOC_LIB ("jemalloc-" __xstr(JEMALLOC_VERSION_MAJOR) "." __xstr(JEMALLOC_VERSION_MINOR) "." __xstr(JEMALLOC_VERSION_BUGFIX))#include #if (JEMALLOC_VERSION_MAJOR == 2 && JEMALLOC_VERSION_MINOR >= 1) || (JEMALLOC_VERSION_MAJOR > 2)#define HAVE_MALLOC_SIZE 1#define zmalloc_size(p) je_malloc_usable_size(p)#else#error "Newer version of jemalloc required"#endif#elif defined(__APPLE__)#include #define HAVE_MALLOC_SIZE 1#define zmalloc_size(p) malloc_size(p)#endif
Redis允许用编译的时候使用第三方的内存分配方案,而这些内存分配方案往往自带存储内存长度的策略,如此一来,Redis就不必自己保存这个长度,反而浪费内存空间。使用第三方内存分配方案,会定义宏定义zmalloc_size
用于获取内存长度,比如这里的jemalloc:
#define zmalloc_size(p) je_malloc_usable_size(p)
void *zcalloc(size_t size) { void *ptr = calloc(1, 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}
在zcalloc
中,size
就已经是真实申请的内存。实现逻辑和zmalloc
差不多,唯一差别仅是申请内存使用的calloc
,内存会被初始化为0。
void *zrealloc(void *ptr, size_t size) {#ifndef HAVE_MALLOC_SIZE void *realptr;#endif size_t oldsize; void *newptr; if (size == 0 && ptr != NULL) { zfree(ptr); return NULL; } if (ptr == NULL) return zmalloc(size);#ifdef HAVE_MALLOC_SIZE oldsize = zmalloc_size(ptr); newptr = realloc(ptr,size); if (!newptr) zmalloc_oom_handler(size); update_zmalloc_stat_free(oldsize); update_zmalloc_stat_alloc(zmalloc_size(newptr)); return newptr;#else realptr = (char*)ptr-PREFIX_SIZE; // 获取真实地址 oldsize = *((size_t*)realptr); // 获取内存长度 newptr = realloc(realptr,size+PREFIX_SIZE); if (!newptr) zmalloc_oom_handler(size); *((size_t*)newptr) = size; update_zmalloc_stat_free(oldsize+PREFIX_SIZE); update_zmalloc_stat_alloc(size+PREFIX_SIZE); return (char*)newptr+PREFIX_SIZE;#endif}
重分配内存,主要看#else
部分。首先通过向低位偏移PREFIX_SIZE
个字节位置,拿到真实的内存地址,然后通过真实地址拿到旧内存长度。真实地址,可以通过realloc函数重分配内存,而旧内存长度则参与到use_memory
的内存长度计算中。
update_zmalloc_stat_free
的作用和update_zmalloc_stat_alloc
相反,是用来对use_memory
做减法操作的。
#define update_zmalloc_stat_free(__n) do { \ size_t _n = (__n); \ if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \ atomicDecr(used_memory,__n); \} while(0)
void zfree(void *ptr) {#ifndef HAVE_MALLOC_SIZE void *realptr; size_t oldsize;#endif if (ptr == NULL) return;#ifdef HAVE_MALLOC_SIZE update_zmalloc_stat_free(zmalloc_size(ptr)); free(ptr);#else realptr = (char*)ptr-PREFIX_SIZE; oldsize = *((size_t*)realptr); update_zmalloc_stat_free(oldsize+PREFIX_SIZE); free(realptr);#endif}
zfree
的时候获取到真实的地址之后计算use_memory
,并释放内存。
转载地址:http://qrme.baihongyu.com/