Redis源码剖析 内存

Stella981
• 阅读 506

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();
}

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
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
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
Java日期时间API系列31
  时间戳是指格林威治时间1970年01月01日00时00分00秒起至现在的总毫秒数,是所有时间的基础,其他时间可以通过时间戳转换得到。Java中本来已经有相关获取时间戳的方法,Java8后增加新的类Instant等专用于处理时间戳问题。 1获取时间戳的方法和性能对比1.1获取时间戳方法Java8以前
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这