redis源码分析 – 内存管理
在 zmalloc.h
这个头文件中,有 USE_TCMALLOC
和 USE_JEMALLOC
这两个宏,分别控制 redis 使用的是 tcmalloc 还是 jemalloc 这两个内存管理器。 tcmalloc 是Google gperftools
里的组件之一。全名是 thread cache malloc
(线程缓存分配器),其内存管理分为线程内存和中央堆两部分。而 jemalloc 是由 FreeBSD
的开发人员 Jason Evans
开发的,在 FreeBSD、NetBSD和 FireFox 中是默认的 malloc,目前是 Maridab 、Tengine、Redis 中默认推荐的内存优化工具。在没有这两个内存管理器的情况下,redis 使用的是 glibc 的 malloc。下面讲解的都是使用 libc 库的源码分析。
#ifndef ZMALLOC_LIB
#define ZMALLOC_LIB "libc"
#endif
redis 对 tcmalloc 和 jemalloc 的函数都进行了封装
#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;
static int zmalloc_thread_safe = 0;
pthread_mutex_t used_memory_mutex = PTHREAD_MUTEX_INITIALIZER;
used_memory
表示系统使用的内存大小,全局维护这么一个变量,说明作者希望通过这个变量来反映内存的使用情况。zmalloc_thread_safe
通过变量名也能看出,这个是控制线程安全模式的变量,后面的 mutex 变量 used_memory_mutex
就是用在线程安全条件下的互斥信号量。
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))
gcc 从 4.1.2
提供了 __sync_*
系列的 built-in
函数,用于提供加减和逻辑运算的原子操作。
// 返回更新前的值
type __sync_fetch_and_add (type *ptr, type value, ...)
type __sync_fetch_and_sub (type *ptr, type value, ...)
type __sync_fetch_and_or (type *ptr, type value, ...)
type __sync_fetch_and_and (type *ptr, type value, ...)
type __sync_fetch_and_xor (type *ptr, type value, ...)
type __sync_fetch_and_nand (type *ptr, type value, ...)
// 返回更新后的值
type __sync_add_and_fetch (type *ptr, type value, ...)
type __sync_sub_and_fetch (type *ptr, type value, ...)
type __sync_or_and_fetch (type *ptr, type value, ...)
type __sync_and_and_fetch (type *ptr, type value, ...)
type __sync_xor_and_fetch (type *ptr, type value, ...)
type __sync_nand_and_fetch (type *ptr, type value, ...)
如果没有定义 HAVE_ATOMIC
这个宏,使用 mutex 实现对 used_memory
的安全操作
#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)
申请
内存申请 zmalloc 函数,调用的仍然是 glibc 的 malloc 函数。
void *zmalloc(size_t size) {
void *ptr = malloc(size+PREFIX_SIZE);
// 当申请的 ptr 为 NULL 时,调用 oom 函数处理方法
if (!ptr) zmalloc_oom_handler(size);
#ifdef HAVE_MALLOC_SIZE
update_zmalloc_stat_alloc(zmalloc_size(ptr));
ret