本文以redis官网能下载的最早版本(0.091)源码为基础,官网下载地址:https://download.redis.io/releases/redis-0.091.tar.gz
redis中的zmalloc主要是对c标准库中的内存操作函数(malloc,free,realloc)进行封装,加入used_memory记录占用内存大小。
每个内存块头部加入一个size_t记录上层申请内存的大小(不包括头部size_t占用空间),使用size_t是为了更好的兼容性,满足不同系统记录内存大小,最后把申请到内存的首地址偏移size_t占用的字节返回上层使用。
/* zmalloc - total amount of allocated memory aware version of malloc()
...
*/
#include <stdlib.h>
#include <string.h>
// 静态变量记录使用内存大小,单位字节
static size_t used_memory = 0;
// 申请内存,对于malloc
void *zmalloc(size_t size) {
// 调用malloc申请内存,大小加上sizeof(size_t)
void *ptr = malloc(size+sizeof(size_t));
// 把size数值存入刚申请到的内存头部
*((size_t*)ptr) = size;
// 更新使用内存大小
used_memory += size+sizeof(size_t);
// 申请到的首地址偏移sizeof(size_t)个字节再返回上一层
// *注:因为指针加减法要对指针进行sizeof(ptr)计算后确定偏移量,这里把ptr转为char*,sizeof(char *)等于1个字节,这样就和sizeof(size_t)返回的字节数相对应
return (char*)ptr+sizeof(size_t);
}
// 重新分配内存,对应realloc
void *zrealloc(void *ptr, size_t size) {
// 真实内存首地址
void *realptr;
// 原大小
size_t oldsize;
// 新的真实内存首地址
void *newptr;
// 如果ptr为空则直接申请新的内存返回
if (ptr == NULL) return zmalloc(size);
// 上层的首地址减去sizeof(size_t)个字节,得到真实申请到的内存首地址,至于为什么转为char*请看zmallo的注释
realptr = (char*)ptr-sizeof(size_t);
// 首地址的内存数据就是原来申请内存大小
oldsize = *((size_t*)realptr);
// 调用realloc重新分配内存,根据realloc特性newptr不一定等于realptr
newptr = realloc(realptr,size+sizeof(size_t));
// 申请不到返回NULL代表失败
if (!newptr) return NULL;
// 把新的size复制到新申请的内存的首地址
*((size_t*)newptr) = size;
// 更新使用内存大小
used_memory -= oldsize;
used_memory += size;
// 和zmalloc一样,首地址偏移后再返回上层
return (char*)newptr+sizeof(size_t);
}
// 释放内存,对应free
void zfree(void *ptr) {
// 真实内存首地址
void *realptr;
// 原内存大小
size_t oldsize;
// 如果为空指针则直接返回
if (ptr == NULL) return;
// 计算真实内存首地址,和zrealloc一样
realptr = (char*)ptr-sizeof(size_t);
// 读取真实内存首地址数据
oldsize = *((size_t*)realptr);
// 更新内存,要加上sizeof(size_t)才是真实申请内存的大小
used_memory -= oldsize+sizeof(size_t);
// 释放内存
free(realptr);
}
// 字符串复制方法
char *zstrdup(const char *s) {
size_t l = strlen(s)+1;
char *p = zmalloc(l);
memcpy(p,s,l);
return p;
}
// 返回使用内存大小
size_t zmalloc_used_memory(void) {
return used_memory;
}