redis学习--内存管理基础

前沿

redis作为目前高性能Nosql数据库,应用在大量的互联网应用基础库中;接下来准备系统的学习下redis 5.0的源代码,首先先看下redis对内存管理做的优化

本篇主要内容

zmalloc

zmalloc是redis的基础,它可以根据环境选择基于不同的原生内存管理库(jemalloc、tcmalloc、libc),不同的基础内存库会导致malloc绑定的函数不同

#if defined(USE_TCMALLOC)
#define malloc(size) tc_malloc(size)
#define calloc(count,size) tc_calloc(count,size)
#define realloc(ptr,size) tc_realloc(ptr,size)
#define free(ptr) tc_free(ptr)
#elif defined(USE_JEMALLOC)
#define malloc(size) je_malloc(size)
#define calloc(count,size) je_calloc(count,size)
#define realloc(ptr,size) je_realloc(ptr,size)
#define free(ptr) je_free(ptr)
#endif

记录堆空间申请大小

redis内存管理模块需要实时知道已经申请了多少空间,通过一个全局变量保存static size_t used_memory = 0;

  • 由于内存分配可能多线程,因此对全局变量要做到原子性;但是不同平台原子性操作的方式不同,有的甚至不支持原子操作,这个时候redis就要统一它们的行为
pthread_mutex_t used_memory_mutex = PTHREAD_MUTEX_INITIALIZER;
……
#if defined(__ATOMIC_RELAXED)
#define update_zmalloc_stat_add(__n) __atomic_add_fetch(&used_memory, (__n), __ATOMIC_RELAXED)
#define update_zmalloc_stat_sub(__n) __atomic_sub_fetch(&used_memory, (__n), __ATOMIC_RELAXED)
#elif defined(HAVE_ATOMIC)
#define update_zmalloc_stat_add(__n) __sync_add_and_fetch(&used_memory, (__n))
#define update_zmalloc_stat_sub(__n) __sync_sub_and_fetch(&used_memory, (__n))
#else
#define update_zmalloc_stat_add(__n) do { \
    pthread_mutex_lock(&used_memory_mutex); \
    used_memory += (__n); \
    pthread_mutex_unlock(&used_memory_mutex); \
} while(0)
 
#define update_zmalloc_stat_sub(__n) do { \
    pthread_mutex_lock(&used_memory_mutex); \
    used_memory -= (__n); \
    pthread_mutex_unlock(&used_memory_mutex); \
} while(0)
#endif
  • 如果不需要考虑线程安全,则可以使用接口关闭安全特性
static int zmalloc_thread_safe = 0;
void zmalloc_enable_thread_safeness(void) {
    zmalloc_thread_safe = 1;
}

内存分配和释放

zmalloc函数

/* 申请内存基础函数 */
void *zmalloc(size_t size) {
/* Redis的内存分配库需要底层库支持通过堆上指针获取该空间大小的功能,但是一些低版本的内存管理库并不支持 
 * zmalloc无论何时都会分配比用户要求大一些的内存
 */
    void *ptr = malloc(size+PREFIX_SIZE);

    if (!ptr) zmalloc_oom_handler(size);
#ifdef HAVE_MALLOC_SIZE
	/* 如果底层内存库支持MALLOC_SIZE函数,则返回给用户所有申请到的内存 */
    update_zmalloc_stat_alloc(zmalloc_size(ptr));
    return ptr;
#else
	/* 如果不支持,则在申请的内存前sizeof(size_t)大小的空间里保存用户需要申请的空间大小size*/
    *((size_t*)ptr) = size;
    update_zmalloc_stat_alloc(size+PREFIX_SIZE);
    return (char*)ptr+PREFIX_SIZE;
#endif
}

zrealloc函数

/* 重新分配空间 */
void *zrealloc(void *ptr, size_t size) {
#ifndef HAVE_MALLOC_SIZE
    void *realptr;
#endif
    size_t oldsize;
    void *newptr;

    if (ptr == NULL) return zmalloc(size);
#ifdef HAVE_MALLOC_SIZE
	/* 通过基础内存库函数获取堆指针ptr对应的内存空间大小 */
    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
	/* 由zmalloc可知,获取本内存真实起始地址需要前移PREFIX_SIZE */
    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
}

附录

参考资料

redis源码:Zmalloc