2024-04-05  阅读(31)
原文作者:文先生的博客 原文地址: http://wenfh2020.com/2020/01/19/redis-zmalloc/

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

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

其中 jemalloctcmalloc 是第三方的实现,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

202404052231192681.png

当前版本 blame

202404052231204392.png


  • 获得系统已使用内存

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

 
    size_t zmalloc_used_memory(void) {
        size_t um;
        atomicGet(used_memory,um);
        return um;
    }

3. 参考


Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。

它的内容包括:

  • 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
  • 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
  • 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
  • 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
  • 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
  • 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
  • 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
  • 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw

目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:

想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询

同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。

阅读全文