2019独角兽企业重金招聘Python工程师标准>>>
临时插入这一块内容,是关于内存分配释放函数的,也就是malloc/calloc/realloc/free这四个,毕竟在很多代码中都有用到。
在redis3.2.6中,对内存分配函数进行了封装,代码主要在zmalloc.h/.c文件里面,有以下几个特性:
1. 支持多种内存处理函数
2. 支持自定义错误处理函数
3. 可获取已分配内存总量
首先说说支持多种内存处理函数:
在3.2.6版本中,支持了tcmalloc、jemalloc和glibc-malloc,需要在编译时通过编译选项指定:
# Default allocator
ifeq ($(uname_S),Linux)
MALLOC=jemalloc
else
MALLOC=libc
endif
# Backwards compatibility for selecting an allocator
ifeq ($(USE_TCMALLOC),yes)
MALLOC=tcmalloc
endif
ifeq ($(USE_TCMALLOC_MINIMAL),yes)
MALLOC=tcmalloc_minimal
endif
ifeq ($(USE_JEMALLOC),yes)
MALLOC=jemalloc
endif
ifeq ($(USE_JEMALLOC),no)
MALLOC=libc
endif
在代码中,则直接根据宏定义进行替换了:
#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
如此,实现了内存操作函数的可配置化。
其次,也支持错误处理函数的自定义,具体表现在允许调用函数zmalloc_set_oom_handler传入函数指针,原型如下:
void zmalloc_set_oom_handler(void (*oom_handler)(size_t));
在每个内存分配函数中,当分配出现问题的时候,都可以调用到,譬如:
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
}
再次,可以动态获取到当前已经使用的内存总量,这是全局性的,通过static size_t used_memory存储。Redis为操作该变量专门添加了一堆保证原子性的宏:
#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)
另外,还可以选择放弃线程安全模式,直接对这个变量进行操作,譬如:
#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; \ //直接对used_memory进行操作
} \
} while(0)
总的来说,zmalloc.h/.c只是redis对于内存操作有一个小封装,实现了内存操作函数的统一入口,没有太多可说的东西,估计这也是《redis的设计与实现》一书没有详说的原因吧。