博客
关于我
Redis(二):Redis的内存分配
阅读量:342 次
发布时间:2019-03-04

本文共 4701 字,大约阅读时间需要 15 分钟。

    Redis在编译时便会指定内存分配器;内存分配器可以是 libc jemalloc或者tcmalloc,默认是jemalloc。在Redis中内存分配主要靠 zmalloc 实现,在zmalloc.c文件实现,在这个文件内还存在其他相关的操作函数。

void *zmalloc(size_t size); // 申请内存void *zcalloc(size_t size); // 申请内存,并初始化为0void *zrealloc(void *ptr, size_t size); // 重申请内存void zfree(void *ptr); // 释放内存

1、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}#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 size_t used_memory = 0;

    通过malloc申请内存时,会多申请一个 PREFIX_SIZE 的大小。这里的 PREFIX_SIZE 就是用于保存内存长度的。内存申请后,将内存长度保存进内存的头部 PREFIX_SIZE 个字节的内存中。并通过update_zmalloc_stat_alloc 调整 use_memory 的记录。

if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1));

    这行代码的作用是判断_n是不是sizeof(long)的整数倍,如果不是则将_n加上差值,使其成为整数倍。本质上是为了内存对齐的操作,实现内存的精准计算。我们需要注意的是,这段宏定义的作用只是为了use_memory能够精准计算内存对齐之后的内存长度,而malloc出来的内存已经实现了内存对齐指针对齐。 

2、PREFIX_SIZE

#ifdef HAVE_MALLOC_SIZE#define PREFIX_SIZE (0)#else#if defined(__sun) || defined(__sparc) || defined(__sparc__)#define PREFIX_SIZE (sizeof(long long))#else#define PREFIX_SIZE (sizeof(size_t))#endif#endif

    分析zmalloc函数发现存在HAVE_MALLOC_SIZE。当定义了HAVE_MALLOC_SIZEPREFIX_SIZE的长度就为0。那这个HAVE_MALLOC_SIZE是什么来头?这个答案可以在zmalloc.h找到:

#if defined(USE_TCMALLOC)#define ZMALLOC_LIB ("tcmalloc-" __xstr(TC_VERSION_MAJOR) "." __xstr(TC_VERSION_MINOR))#include 
#if (TC_VERSION_MAJOR == 1 && TC_VERSION_MINOR >= 6) || (TC_VERSION_MAJOR > 1)#define HAVE_MALLOC_SIZE 1#define zmalloc_size(p) tc_malloc_size(p)#else#error "Newer version of tcmalloc required"#endif#elif defined(USE_JEMALLOC)#define ZMALLOC_LIB ("jemalloc-" __xstr(JEMALLOC_VERSION_MAJOR) "." __xstr(JEMALLOC_VERSION_MINOR) "." __xstr(JEMALLOC_VERSION_BUGFIX))#include
#if (JEMALLOC_VERSION_MAJOR == 2 && JEMALLOC_VERSION_MINOR >= 1) || (JEMALLOC_VERSION_MAJOR > 2)#define HAVE_MALLOC_SIZE 1#define zmalloc_size(p) je_malloc_usable_size(p)#else#error "Newer version of jemalloc required"#endif#elif defined(__APPLE__)#include
#define HAVE_MALLOC_SIZE 1#define zmalloc_size(p) malloc_size(p)#endif

    Redis允许用编译的时候使用第三方的内存分配方案,而这些内存分配方案往往自带存储内存长度的策略,如此一来,Redis就不必自己保存这个长度,反而浪费内存空间。使用第三方内存分配方案,会定义宏定义zmalloc_size用于获取内存长度,比如这里的jemalloc:

#define zmalloc_size(p) je_malloc_usable_size(p)

3、zcalloc

void *zcalloc(size_t size) {    void *ptr = calloc(1, 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}

    在zcalloc中,size就已经是真实申请的内存。实现逻辑和zmalloc差不多,唯一差别仅是申请内存使用的calloc,内存会被初始化为0。 

4、zrealloc

void *zrealloc(void *ptr, size_t size) {#ifndef HAVE_MALLOC_SIZE    void *realptr;#endif    size_t oldsize;    void *newptr;    if (size == 0 && ptr != NULL) {        zfree(ptr);        return NULL;    }    if (ptr == NULL) return zmalloc(size);#ifdef HAVE_MALLOC_SIZE    oldsize = zmalloc_size(ptr);    newptr = realloc(ptr,size);    if (!newptr) zmalloc_oom_handler(size);    update_zmalloc_stat_free(oldsize);    update_zmalloc_stat_alloc(zmalloc_size(newptr));    return newptr;#else    realptr = (char*)ptr-PREFIX_SIZE; // 获取真实地址    oldsize = *((size_t*)realptr); // 获取内存长度    newptr = realloc(realptr,size+PREFIX_SIZE);    if (!newptr) zmalloc_oom_handler(size);    *((size_t*)newptr) = size;    update_zmalloc_stat_free(oldsize+PREFIX_SIZE);    update_zmalloc_stat_alloc(size+PREFIX_SIZE);    return (char*)newptr+PREFIX_SIZE;#endif}

    重分配内存,主要看#else部分。首先通过向低位偏移PREFIX_SIZE个字节位置,拿到真实的内存地址,然后通过真实地址拿到旧内存长度。真实地址,可以通过realloc函数重分配内存,而旧内存长度则参与到use_memory的内存长度计算中。

    update_zmalloc_stat_free的作用和update_zmalloc_stat_alloc相反,是用来对use_memory做减法操作的。

#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)

5、zfree 

void zfree(void *ptr) {#ifndef HAVE_MALLOC_SIZE    void *realptr;    size_t oldsize;#endif    if (ptr == NULL) return;#ifdef HAVE_MALLOC_SIZE    update_zmalloc_stat_free(zmalloc_size(ptr));    free(ptr);#else    realptr = (char*)ptr-PREFIX_SIZE;    oldsize = *((size_t*)realptr);    update_zmalloc_stat_free(oldsize+PREFIX_SIZE);    free(realptr);#endif}

    zfree的时候获取到真实的地址之后计算use_memory,并释放内存。

转载地址:http://qrme.baihongyu.com/

你可能感兴趣的文章
LeetCode75 颜色分类 (三路快排C++实现与应用)
查看>>
docker基础:容器生命周期管理命令
查看>>
Shell脚本学习指南
查看>>
C#开发BIMFACE系列35 服务端API之模型对比6:获取模型构建对比分类树
查看>>
C# 规范建议
查看>>
.NET 5.0正式发布,新功能特性(翻译)
查看>>
一些有趣的线段树玩法
查看>>
C语言+easyX图形库的推箱子实现
查看>>
反汇编-流程控制语句-2-循环控制语句分析
查看>>
调试vs2019代码的流程
查看>>
游戏外挂基础-概述
查看>>
脱壳与加壳-加壳-6-代码实现加密导入表
查看>>
Typora配置PicGo时,提示Failed to fetch
查看>>
ASP.NET Core 使用 URL Rewrite 中间件实现 HTTP 重定向到 HTTPS
查看>>
ASP.NET CORE MVC 实现减号分隔(Kebab case)样式的 URL
查看>>
SQL优化 MySQL版 -分析explain SQL执行计划与笛卡尔积
查看>>
bcolz的新操作
查看>>
Linux的s、t、i、a权限(转)
查看>>
zmq的send
查看>>
C++中的delete加深认识
查看>>