Redis源码剖析 内存

Redis通过自己的方法管理内存,主要方法有zmalloc(),zrealloc(),zcalloc()和zfree(),
分别对应C中的malloc(),realloc(),calloc()和free().

redis自己管理内存的好处主要有两个:

1、可以利用内存池等手段提高内存分配的性能;

2、’可以掌握更多的内存信息,以便于redis虚拟内存等功能中,决定何时将数据swap到磁盘。

malloc()分配一块指定大小的内存区域,并返回指向区域开头的指针,若分配失败则返回NULL;

calloc()与malloc()一样分配一块指定大小的内存区域,成功时返回区域头指针,失败时返回NULL。
区别在于,calloc()输入的参数为count何size,即分配的项的数目,及每一项的大小。calloc()
成功分配内存空间后,会将空间内所有值置0.

realloc()修改已分配内存块的 大小。若已分配的内存块后没有足够的空间用于扩展内存块,则
重新申请一块满足需求的内存块,并将旧的数据拷贝到新的位置,释放旧的内存块,返回指向新的
内存块的指针 ;否则直接扩展原有的内存块。若分配失败,返回NULL。
free()释放以分配的内存块。_

内存分配
在redis中,如果系统包含TCMALLOC,则会使用 tc_malloc(0等TCMALLOC中的方法替代malloc()
等原有的分配内存方法。_

static size_t used_memoryy = 0;
static int zmalloc_thread_safe = 0;

void *zmalloc(size_t size) {
/*
redis中除了申请需要的size外还需要申请PREFIX_SIZE大小区域记录内存的长度
redis会使用宏函数updata_zmalloc_stat_alloc()记录申请的内存块的 相关信息
以便监控内存使用状况;当内存块被zfree()释放时,根据头部的信息可以快速
地获知被释放的内存区域的长度,然后通过宏函数update_zmalloc_stat_free()标记
释放。源代码中,若系统支持malloc_size()方法,则会使用它返回指针所指向的内存块
的大小(Mac OS X 10.4以上支持该方法[3])。
*/
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
}

/*
有疑惑的是,在支持malloc_size()的系统中,为何还要多申请PREFIX_SIZE的内存?
#ifdef HAVE_MALLOC_SIZE
#define PREFIX_SIZE (0)       //此时PREFIX_SIZE大小设置为0
#if defined(__sun) || defined(__sparc) || defined(__sparc__)
#define PREFIX_SIZE (sizeof(long long))
#else
#define PREFIX_SIZE (sizeof(size_t))
#endif
#endif
*/

在updata_zmalloc_stat_alloc(_n) 中首先将要分配的空间与内存对齐,然后会
根据宏zmalloc_thread_safe判断是否需要对内存信息记录表的相关操作加锁。虽然
Redis在大部分场景中是单线程读写的,即thread_safe的,但启用虚拟内存(VM),或持
久化dump到磁盘等操作时会启动多线程,因此在多线程模式中,需要对部分操作加锁。

#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; \
} \
} while(0)

若是内存分配失败时则会调用自己实现的内存分配失败函数,抛出异常
static void zmalloc_default_oom(size_t size) {
fprintf(stderr, "zmalloc: Out of memory trying to allocate %zu bytes\n",
size);
fflush(stderr);
abort();
}

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

Java日期时间API系列31

  时间戳是指格林威治时间1970年01月01日00时00分00秒起至现在的总毫秒数,是所有时间的基础,其他时间可以通过时间戳转换得到。Java中本来已经有相关获取时间戳的方法,Java8后增加新的类Instant等专用于处理时间戳问题。 1获取时间戳的方法和性能对比1.1获取时间戳方法Java8以前

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

Redis源码剖析 内存 - HelloWorld