redis可选内存管理方式为tcmalloc或jemalloc,用宏控制,一般用jemalloc性能最好,底层调用remalloc接口,redis的内存管理模块只是封装函数,内存管理在zmalloc.h和zmalloc.c中。
zmalloc.c中定义一个全局变量used_memory和两个用于管理user_memory的宏,如下
#define update_zmalloc_stat_alloc(__n) do { \
size_t _n = (__n); \
if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \
atomicIncr(used_memory,__n); \
} while(0)
#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)
static size_t used_memory = 0;
pthread_mutex_t used_memory_mutex = PTHREAD_MUTEX_INITIALIZER;
其中atomicIncr和atomicDecr是原子操作,通过互斥锁实现,内存补齐使用位操作以保证性能,used_memory保存的是实际使用的内存大小
内存分配函数zmalloc:
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
}
给ptr分配内存时,增加size_t大小内存,用于保存实际使用的内存大小,返回给调用者的指针越过这个大小的地址。其余内存管理函数calloc和realloc等实现方式和malloc类似
进程内存管理:
size_t zmalloc_get_rss(void) {
int page = sysconf(_SC_PAGESIZE);
size_t rss;
char buf[4096];
char filename[256];
int fd, count;
char *p, *x;
snprintf(filename,256,"/proc/%d/stat",getpid());
if ((fd = open(filename,O_RDONLY)) == -1) return 0;
if (read(fd,buf,4096) <= 0) {
close(fd);
return 0;
}
close(fd);
p = buf;
count = 23; /* RSS is the 24th field in /proc/<pid>/stat */
while(p && count--) {
p = strchr(p,' ');
if (p) p++;
}
if (!p) return 0;
x = strchr(p,' ');
if (!x) return 0;
*x = '\0';
rss = strtoll(p,NULL,10);
rss *= page;
return rss;
}
通过读取/proc/pid/stat中的rss字段,涉及文件读取,显然性能很差。内存碎片率用rss/used_memory计算,此外zmalloc模块还提供了读取smap等内存接口,具体含义参见linux进程内存管理机制