内存分配是程序需要解决的一个大问题,也应该是剖析一个程序的源码最基础最关键的部分之一。
Redis的内存分配主要是在C语言中对内存处理的函数:malloc、realloc、free的基础加了一些封装和异常的处理。
其特点主要有以下三点:
- 添加对使用内存的统计,在分配和释放内存的时候都会更新记录使用内存的量
- 支持线程安全模式,通过锁的机制对use_memory进行控制,避免其出现脏数据的可能
- 增添对内存溢出的处理
首先Redis源码中与内存分配相关的以下全局变量:1
2
3static size_t used_memory = 0; // 使用的内存大小
static int zmalloc_thread_safe = 0; // 线程安全模式状态
pthread_mutex_t used_memory_mutex = PTHREAD_MUTEX_INITIALIZER; // 为此服务器
内存分配相关功能函数
Redis中和内存分配相关的功能函数主要有以下这些:1
2
3
4
5
6
7
8
9
10
11void *zmalloc(size_t size);
void *zcalloc(size_t size);
void *zrealloc(void *ptr, size_t size);
void zfree(void *ptr);
char *zstrdup(const char *s); // 封装的字符复制函数
size_t zmalloc_used_memory(void); // 获取使用的内存大小
void zmalloc_enable_thread_safeness(void); // 开启线程安全模式
void zmalloc_set_oom_handler(void (*oom_handler)(size_t)); // 自定义的内存溢出处理方法
float zmalloc_get_fragmentation_ratio(size_t rss); // 使用内存和所给内存之比
size_t zmalloc_get_rss(void); // 获取rss信息
size_t zmalloc_get_private_dirty(void); // 获取实际物理分配的内存
内存申请与调整
Redis中和内存申请相关的函数主要是zmalloc、zcalloc和zrealloc,分别是对C语言中的malloc、calloc和realloc的封装。
1 | // 对malloc进行封装,加上异常处理和内存统计 |
首先要注意的是对内存溢出的处理,可以自定义处理函数,其默认的处理方式如下:
1 | // 默认溢出处理方法 |
然后我们可以看到每次在调整内存的时候,都会对全局变量use_memory进行改变,调整是以sizeof(long)
的整数倍进行调整的。在改变use_memory的时候会判断是否在线程安全模式下。
1 | // 更新使用的内存数量值 |
内存释放
内存释放部分和申请部分类似,主要是对free函数的封装。
1 | // 获取内存占用空间大小 |
对use_memory的操作:1
2
3
4
5
6
7
8
9
10
11
12// 释放内存以后对内存空间统计变量的改变
size_t _n = (__n); \
// 以sizeof(long)的整数倍进行调整
if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \
// 线程安全模式
if (zmalloc_thread_safe) { \
update_zmalloc_stat_sub(_n); \
} else { \
used_memory -= _n; \
} \
} while(0)
辅助函数
除了最基本的一些内存分配的函数以外,还实现了一些辅助函数(如复制字符串、获取已经使用内存的大小):
1 | // 复制字符串操作 |