[redis 源码走读] - zmalloc

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析


阶段4、深入jdk其余源码解析


阶段5、深入jvm源码解析

码哥源码部分

码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】

码哥讲源码【炸雷啦!炸雷啦!黄光头他终于跑路啦!】

码哥讲源码-【jvm课程前置知识及c/c++调试环境搭建】

​​​​​​码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】

码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】

码哥讲源码【你水不是你的错,但是你胡说八道就是你不对了!】

码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】

终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!

打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】

redis 内存管理实现,有三种方案:

  1. jemalloc (谷歌)
  2. tcmalloc (facebook)
  3. libc (系统)

其中 jemalloc, tcmalloc 是第三方的实现,libc 的实现做了一些简单的封装。

1. 内存池方案

 
    // 理解宏对相关库的引入使用。
    #if defined(USE_TCMALLOC)
    #define ZMALLOC_LIB ("tcmalloc-" __xstr(TC_VERSION_MAJOR) "." __xstr(TC_VERSION_MINOR))
    #include <google/tcmalloc.h>
    #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 <jemalloc/jemalloc.h>
    #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 <malloc/malloc.h>
    #define HAVE_MALLOC_SIZE 1
    #define zmalloc_size(p) malloc_size(p)
    #endif
    
    #ifndef ZMALLOC_LIB
    #define ZMALLOC_LIB "libc"
    #ifdef __GLIBC__
    #include <malloc.h>
    #define HAVE_MALLOC_SIZE 1
    #define zmalloc_size(p) malloc_usable_size(p)
    #endif
    #endif

2. 核心接口

  • 内存管理 如果是 libc 实现的内存管理,内存分配会加一个前缀,保存内存长度。有点像 nginx 的字符串结构。分配内存返回内容指针,释放内存,指针要从数据部分移动到内存长度部分。
 
    // nginx 字符串结构
    typedef struct {
        size_t      len;
        u_char     *data;
    } ngx_str_t;
 
    #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
    
    // 分配内存
    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
    }
    
    // 释放内存
    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
    }
  • 内存对齐和统计 used_memory 统计内存使用 分配内存,内存对齐是为了提高 cpu 效率。但是 update_zmalloc_stat_alloc
 
    #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)

这个函数的实现让人费解,代码对 _n 进行操作,最后却保存了 __n 。github 上虽然提出了这个问题,貌似没有得到解决.

历史版本 blame

当前版本 blame


  • 获得系统已使用内存

redis 内存申请几乎都调用 zmalloc 接口,每次申请和回收都会被 used_memory 记录起来。当系统处理 maxmemory 的时候,就要知道系统使用了多少内存,从而进行一些回收数据的策略。

 
    size_t zmalloc_used_memory(void) {
        size_t um;
        atomicGet(used_memory,um);
        return um;
    }
  • 28
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值