深入解读Redis之内存模型解析-zmalloc

Redis源码之内存模型解析-zmalloc

当前分析Redis版本为6.2,需要注意。

由于上一次分析了SDS,发现有关内存管理都是使用的zmalloc,虽然中间取了次别名,与此区别。如果接着看其他数据结构,估计还是zmalloc。藉此,将Redis的内存模型分析直接提前,就是为了后面看其他数据结构,更加清晰。为此,先看一下SDS有关内存,sdsalloc.h

#include "zmalloc.h"
#define s_malloc zmalloc
#define s_realloc zrealloc
#define s_trymalloc ztrymalloc
#define s_tryrealloc ztryrealloc
#define s_free zfree
#define s_malloc_usable zmalloc_usable
#define s_realloc_usable zrealloc_usable
#define s_trymalloc_usable ztrymalloc_usable
#define s_tryrealloc_usable ztryrealloc_usable
#define s_free_usable zfree_usable

无一例外,全是基于zmalloc的。接下来,切入正题,直接看头文件(src/zmalloc.h)的相关常量或原型之类的定义吧。

函数原型
  • zmalloc void *zmalloc(size_t size) 基于 malloc 实现的动态内存分配函数;
  • zcalloc void *zcalloc(size_t size) 基于 calloc 实现的动态内存分配函数,和 malloc 的区别是,该可对分配内存进行初始化工作;
  • zrealloc void *zrealloc(void *ptr, size_t size) 重新分配指定内存,并初始化;
  • ztrymalloc void *ztrymalloc(size_t size) 尝试分配内存;
  • ztrycalloc void *ztrycalloc(size_t size) 尝试分配内存并初始化;
  • ztryrealloc void *ztryrealloc(void *ptr, size_t size) 尝试重新分配指定内存;
  • zfree void zfree(void *ptr) 释放指定内存;
  • zmalloc_usable void *zmalloc_usable(size_t size, size_t *usable) 动态内存分配并返回可用内存大小;
  • zcalloc_usable void *zcalloc_usable(size_t size, size_t *usable)
  • zrealloc_usable void *zrealloc_usable(void *ptr, size_t size, size_t *usable)
  • ztrymalloc_usable void *ztrymalloc_usable(size_t size, size_t *usable)
  • ztrycalloc_usable void *ztrycalloc_usable(size_t size, size_t *usable)
  • ztryrealloc_usable void *ztryrealloc_usable(void *ptr, size_t size, size_t *usable)
  • zfree_usable void zfree_usable(void *ptr, size_t *usable)
  • zstrdup char *zstrdup(const char *s) 拷贝指定字符串;
  • zmalloc_used_memory size_t zmalloc_used_memory(void); 获取已使用内存大小;
  • zmalloc_set_oom_handler void zmalloc_set_oom_handler(void (*oom_handler)(size_t)) 设置内存溢出句柄;
  • zmalloc_get_rss size_t zmalloc_get_rss(void) 获取实际使用内存大小,RSS(Resident Set Size)实际使用物理内存(包含共享库占用的内存);
  • zmalloc_get_allocator_info int zmalloc_get_allocator_info(size_t *allocated, size_t *active, size_t *resident) 获取指定内存状态等信息;
  • set_jemalloc_bg_thread void set_jemalloc_bg_thread(int enable) 设置后台进程;
  • jemalloc_purge int jemalloc_purge() 释放未使用的内存;
  • zmalloc_get_private_dirty size_t zmalloc_get_private_dirty(long pid) 获取指定进程被标记为 私有脏页大小;
  • zmalloc_get_smap_bytes_by_field size_t zmalloc_get_smap_bytes_by_field(char *field, long pid)libproc 接口获取指定字段的统计;
  • zmalloc_get_memory_size size_t zmalloc_get_memory_size(void) 获取物理内存的大小;
  • zlibc_free void zlibc_free(void *ptr) 释放内存;

还有一些零零散散的函数,是根据不同环境定义,遇到再具体分析。

函数实现

以函数出现的先后顺序分析,如果涉及到某些常量再做具体说明。

zlibc_free

这是在 zmalloc.c 源文件引入 zmalloc.h 头文件之前定义的使用libc的free接口释放内存的包装函数,在前面定义,主要避免影响到 Jemalloc 或其他非标准动态内存分配模型的 free 的接口。其实现是直接调用 free

zmalloc_default_oom

默认内存溢出处理句柄。

static void zmalloc_default_oom(size_t size) {
   
    fprintf(stderr, "zmalloc: Out of memory trying to allocate %zu bytes\n",
        size); // 直接向标准错误输出
    fflush(stderr); // 刷新标准错误输出缓冲区
    abort(); // 然后 直接中断进程
}
ztrymalloc_usable

尝试动态内存分配,如果分配失败就返回null,参数usable表示可用内存大小。采用 malloc 实现的。

void *ztrymalloc_usable(size_t size, size_t *usable) {
   
    // 断言size是否溢出 
    // #define ASSERT_NO_SIZE_OVERFLOW(sz) assert((sz) + PREFIX_SIZE > (sz))
    // 由于在HAVE_MALLOC_SIZE常量(系统存在malloc_size函数)定义的情况下,PREFIX_SIZE常量为0
    // 所以,直接就是 #define ASSERT_NO_SIZE_OVERFLOW(sz)
    ASSERT_NO_SIZE_OVERFLOW(size);
    // MALLOC_MIN_SIZE 也是个宏定义 
    // #define MALLOC_MIN_SIZE(x) ((x) > 0 ? (x) : sizeof(long))
    // 在使用 libc 动态内存分配时,使用一个最小可分配大小去适配 jemalloc 内存模型
    // 分配时,需要带上前缀大小,也就是 PREFIX_SIZE
    void *ptr = malloc(MALLOC_MIN_SIZE(size)+PREFIX_SIZE);

    if (!ptr) return NULL;
#ifdef HAVE_MALLOC_SIZE // 系统存在 malloc_size 函数的情况
    size = zmalloc_size(ptr); // 获取指针指向的内存大小
    // #define update_zmalloc_stat_alloc(__n) atomicIncr(used_memory,(__n))
    // 更新原子类静态变量 used_memory(内存总使用大小)的值,互斥操作
    update_zmalloc_stat_alloc(size);
    if (usable) *usable = size; // 更新可用内存
    return ptr;
#else
    // 给内存的指定头部填写值,值为内存的长度
    // ptr指向由类型为size_t的一个或多个对象组成的数组元素 或ptr的前size_t个字节空间
    *((size_t*)ptr) = size;
    update_zmalloc_stat_alloc(size+PREFIX_SIZE);
    if (usable) *usable = size;
    return (char*)ptr+PREFIX_SIZE;
#endif
}
zmalloc

直接调用 ztrymalloc_usable 分配内存,成功返回对应指针,失败就调用 zmalloc_oom_handler 报错并中断进程,比较暴力。

ztrymalloc

zmalloc 的友好实现,失败后将处理交由函数调用方,也就是返回null的情况。很 try 的味道。

zmalloc_usable

zmalloc 的变形,多了 usable 参数,记录可用内存大小。

zmalloc_no_tcache

绕过线程缓存,直接在 JemallocArena bin 分配。仅在 HAVE_DEFRAG 定义时定义,这是碎片整理的宏定义,在使用 jemalloc 内存模型并存在 JEMALLOC_FRAG_HINT 定义时定义。

void *zmalloc_no_tcache(size_t size) {
   
    ASSERT_NO_SIZE_OVERFLOW(size);
    // Jemalloc 的分配函数?
    void *ptr = mallocx(size+PREFIX_SIZE, MALLOCX_TCACHE_NONE);
    if (!ptr) zmalloc_oom_handler(size);
    update_zmalloc_stat_alloc(zmalloc_size(ptr));
    return ptr;
}
zfree_no_tcache

zmalloc_no_tcache 对应的释放函数。

void zfree_no_tcache(void *ptr) {
   
    if (ptr == NULL) return;
    update_zmalloc_stat_free(zmalloc_size(ptr));
    dallocx(ptr, MALLOCX_TCACHE_NONE);
}
ztrycalloc_usable

尝试动态内存分配并初始化,基于 calloc 实现。失败返回 null

void *ztrycalloc_usable(size_t size, size_t *usable) {
   
    ASSERT_NO_SIZE_OVERFLOW(size);
    // calloc 分配内存,并将分配的内存设置为0
    // 第一个参数 被分配的元素个数,第二个参数 元素的大小
    void *ptr = calloc(1, MALLOC_MIN_SIZE(size)+PREFIX_SIZE);
    if (ptr == NULL) return NULL;

    // 后面同 ztrymalloc_usable 一致,根据 HAVE_MALLOC_
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值