主要函数

  • void *zmalloc(size_t size);

  • void *zcalloc(size_t size);

  • void *zrealloc(void *ptr, size_t size);

  • void zfree(void *ptr);

  • char *zstrdup(const char *s);

  • size_t zmalloc_used_memory(void);

  • void zmalloc_set_oom_handler(void (*oom_handler)(size_t));

  • size_t zmalloc_get_rss(void);

  • int zmalloc_get_allocator_info(size_t *allocated, size_t *active, size_t *resident);

  • size_t zmalloc_get_private_dirty(long pid);

  • size_t zmalloc_get_smap_bytes_by_field(char *field, long pid);

  • size_t zmalloc_get_memory_size(void);

  • void zlibc_free(void *ptr);

void *zmalloc(size_t size)

  1. void *zmalloc(size_t size) {
  2. void *ptr = malloc(size+PREFIX_SIZE);
  3. if (!ptr) zmalloc_oom_handler(size);
  4. #ifdef HAVE_MALLOC_SIZE
  5. update_zmalloc_stat_alloc(zmalloc_size(ptr));
  6. return ptr;
  7. #else
  8. *((size_t*)ptr) = size;
  9. update_zmalloc_stat_alloc(size+PREFIX_SIZE);
  10. return (char*)ptr+PREFIX_SIZE;
  11. #endif
  12. }

参数size是需要分配的空间大小。事实上我们需要分配的空间大小为size+PREFIX_SIZE。PREFIX_SIZE是根据平台的不同和HAVE_MALLOC_SIZE宏定义控制的。如果malloc()函数调用失败,就会调用zmalloc_oom_handler()函数来打印异常,并且会终止函数,zmalloc_oom_handler其实是一个函数指针,真正调用的函数是zmalloc_default_oom(),zmalloc_default_oom()函数源码如下:

  1. static void zmalloc_default_oom(size_t size) {
  2. fprintf(stderr, "zmalloc: Out of memory trying to allocate %zu bytes\n",
  3. size);
  4. fflush(stderr);
  5. abort();
  6. }
  7. static void (*zmalloc_oom_handler)(size_t) = zmalloc_default_oom;

内存分配成功之后,会依据HAVE_MALLOC_SIZE的控制对前八个字节操作,用以记录分配内存的长度,会在update_zmalloc_stat_alloc()宏定义函数中更新used_memory这个静态变量的值,update_zmalloc_stat_alloc()源码如下:

  1. #define update_zmalloc_stat_alloc(__n) do { \
  2. size_t _n = (__n); \
  3. if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \
  4. atomicIncr(used_memory,__n); \
  5. } while(0)
  1. if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1));

这一行是为了将不为sizeof(long)的_n对sizeof(long)补齐

atomicIncr(used_memory,__n);会调用__atomic_add_fetch(&var,(count),__ATOMIC_RELAXED);用于保证更新used_memory变量的操作是一个原子操作。

void *zcalloc(size_t size)

  1. void *zcalloc(size_t size) {
  2. void *ptr = calloc(1, size+PREFIX_SIZE);
  3. if (!ptr) zmalloc_oom_handler(size);
  4. #ifdef HAVE_MALLOC_SIZE
  5. update_zmalloc_stat_alloc(zmalloc_size(ptr));
  6. return ptr;
  7. #else
  8. *((size_t*)ptr) = size;
  9. update_zmalloc_stat_alloc(size+PREFIX_SIZE);
  10. return (char*)ptr+PREFIX_SIZE;
  11. #endif
  12. }

zcalloc函数和zmalloc函数处理的思路很是相似,就不做太多的解释了

void *zrealloc(void *ptr, size_t size)

  1. void *zrealloc(void *ptr, size_t size) {
  2. // 如果没有定义HAVE_MALLOC_SIZE,就说明PREFIX_SIZE宏定义不为0,那么ptr并不是该段内存真正的开始地址
  3. #ifndef HAVE_MALLOC_SIZE
  4. void *realptr;
  5. #endif
  6. size_t oldsize;
  7. void *newptr;
  8. if (ptr == NULL) return zmalloc(size);
  9. // 根据HAVE_MALLOC_SIZE宏定义,oldsize,newptr获取方式不一样,以及更新used_memory的细节
  10. #ifdef HAVE_MALLOC_SIZE
  11. oldsize = zmalloc_size(ptr);
  12. newptr = realloc(ptr,size);
  13. if (!newptr) zmalloc_oom_handler(size);
  14. update_zmalloc_stat_free(oldsize);
  15. update_zmalloc_stat_alloc(zmalloc_size(newptr));
  16. return newptr;
  17. #else
  18. realptr = (char*)ptr-PREFIX_SIZE;
  19. oldsize = *((size_t*)realptr);
  20. newptr = realloc(realptr,size+PREFIX_SIZE);
  21. if (!newptr) zmalloc_oom_handler(size);
  22. *((size_t*)newptr) = size;
  23. update_zmalloc_stat_free(oldsize+PREFIX_SIZE);
  24. update_zmalloc_stat_alloc(size+PREFIX_SIZE);
  25. return (char*)newptr+PREFIX_SIZE;
  26. #endif
  27. }

大致思路就是根据新的size进行重新分配内存,并且对used_memory变量进行更新。只不过获取原内存大小方式不一样,根据HAVE_MALLOC_SIZE进行区分。

void zfree(void *ptr)

  1. void zfree(void *ptr) {
  2. #ifndef HAVE_MALLOC_SIZE
  3. void *realptr;
  4. size_t oldsize;
  5. #endif
  6. if (ptr == NULL) return;
  7. #ifdef HAVE_MALLOC_SIZE
  8. update_zmalloc_stat_free(zmalloc_size(ptr));
  9. free(ptr);
  10. #else
  11. realptr = (char*)ptr-PREFIX_SIZE;
  12. oldsize = *((size_t*)realptr);
  13. update_zmalloc_stat_free(oldsize+PREFIX_SIZE);
  14. free(realptr);
  15. #endif
  16. }

其实zfree函数和zrealloc函数做法差不到太多,都是对oldsize和realptr对HAVE_MALLOC_SIZE有无声明分别进行操作。

char *zstrdup(const char *s)

  1. char *zstrdup(const char *s) {
  2. size_t l = strlen(s)+1;
  3. char *p = zmalloc(l);
  4. memcpy(p,s,l);
  5. return p;
  6. }

该函数是创建一个字符串副本

size_t zmalloc_used_memory(void)

  1. size_t zmalloc_used_memory(void) {
  2. size_t um;
  3. atomicGet(used_memory,um);
  4. return um;
  5. }

获取used_memory变量的值,主要保证原子操作(在atomicGet(used_memory,um);中保证)

void zmalloc_set_oom_handler(void (*oom_handler)(size_t))

  1. void zmalloc_set_oom_handler(void (*oom_handler)(size_t)) {
  2. zmalloc_oom_handler = oom_handler;
  3. }

主要用来设置内存分配失败处理函数指针zmalloc_oom_handler的值

size_t zmalloc_get_rss(void)

  1. size_t zmalloc_get_rss(void) {
  2. int page = sysconf(_SC_PAGESIZE);
  3. size_t rss;
  4. char buf[4096];
  5. char filename[256];
  6. int fd, count;
  7. char *p, *x;
  8. snprintf(filename,256,"/proc/%d/stat",getpid());
  9. if ((fd = open(filename,O_RDONLY)) == -1) return 0;
  10. if (read(fd,buf,4096) <= 0) {
  11. close(fd);
  12. return 0;
  13. }
  14. close(fd);
  15. p = buf;
  16. count = 23; /* RSS is the 24th field in /proc/<pid>/stat */
  17. while(p && count--) {
  18. p = strchr(p,' ');
  19. if (p) p++;
  20. }
  21. if (!p) return 0;
  22. x = strchr(p,' ');
  23. if (!x) return 0;
  24. *x = '\0';
  25. rss = strtoll(p,NULL,10);
  26. rss *= page;
  27. return rss;
  28. }

返回驻留集大小

int zmalloc_get_allocator_info(size_t *allocated, size_t *active, size_t *resident)

  1. #if defined(USE_JEMALLOC)
  2. int zmalloc_get_allocator_info(size_t *allocated,
  3. size_t *active,
  4. size_t *resident) {
  5. uint64_t epoch = 1;
  6. size_t sz;
  7. *allocated = *resident = *active = 0;
  8. /* Update the statistics cached by mallctl. */
  9. sz = sizeof(epoch);
  10. je_mallctl("epoch", &epoch, &sz, &epoch, sz);
  11. sz = sizeof(size_t);
  12. /* Unlike RSS, this does not include RSS from shared libraries and other non
  13. * heap mappings. */
  14. je_mallctl("stats.resident", resident, &sz, NULL, 0);
  15. /* Unlike resident, this doesn't not include the pages jemalloc reserves
  16. * for re-use (purge will clean that). */
  17. je_mallctl("stats.active", active, &sz, NULL, 0);
  18. /* Unlike zmalloc_used_memory, this matches the stats.resident by taking
  19. * into account all allocations done by this process (not only zmalloc). */
  20. je_mallctl("stats.allocated", allocated, &sz, NULL, 0);
  21. return 1;
  22. }
  23. #else
  24. int zmalloc_get_allocator_info(size_t *allocated,
  25. size_t *active,
  26. size_t *resident) {
  27. *allocated = *resident = *active = 0;
  28. return 1;
  29. }
  30. #endif

获取分配器的信息,主要在使用jemalloc前提下使用,获取jemalloc分配的信息,详细信息可在http://jemalloc.net/jemalloc.3.html查阅

size_t zmalloc_get_smap_bytes_by_field(char *field, long pid)

  1. #if defined(HAVE_PROC_SMAPS)
  2. size_t zmalloc_get_smap_bytes_by_field(char *field, long pid) {
  3. char line[1024];
  4. size_t bytes = 0;
  5. int flen = strlen(field);
  6. FILE *fp;
  7. if (pid == -1) {
  8. // /proc/pid/smaps反应了运行时的进程的内存影响,系统的运行时库(so),堆,栈信息均可在其中看到。
  9. fp = fopen("/proc/self/smaps","r");
  10. } else {
  11. char filename[128];
  12. snprintf(filename,sizeof(filename),"/proc/%ld/smaps",pid);
  13. fp = fopen(filename,"r");
  14. }
  15. if (!fp) return 0;
  16. while(fgets(line,sizeof(line),fp) != NULL) {
  17. if (strncmp(line,field,flen) == 0) {
  18. char *p = strchr(line,'k');
  19. if (p) {
  20. *p = '\0';
  21. bytes += strtol(line+flen,NULL,10) * 1024;
  22. }
  23. }
  24. }
  25. fclose(fp);
  26. return bytes;
  27. }
  28. #else
  29. size_t zmalloc_get_smap_bytes_by_field(char *field, long pid) {
  30. ((void) field);
  31. ((void) pid);
  32. return 0;
  33. }
  34. #endif

获取/proc/pid/smaps中某一个field的字节大小

size_t zmalloc_get_private_dirty(long pid)

  1. size_t zmalloc_get_private_dirty(long pid) {
  2. return zmalloc_get_smap_bytes_by_field("Private_Dirty:",pid);
  3. }

获取Rss中已改写的私有页面页面大小

size_t zmalloc_get_memory_size(void)

  1. size_t zmalloc_get_memory_size(void) {
  2. #if defined(__unix__) || defined(__unix) || defined(unix) || \
  3. (defined(__APPLE__) && defined(__MACH__))
  4. #if defined(CTL_HW) && (defined(HW_MEMSIZE) || defined(HW_PHYSMEM64))
  5. int mib[2];
  6. mib[0] = CTL_HW;
  7. #if defined(HW_MEMSIZE)
  8. mib[1] = HW_MEMSIZE; /* OSX. --------------------- */
  9. #elif defined(HW_PHYSMEM64)
  10. mib[1] = HW_PHYSMEM64; /* NetBSD, OpenBSD. --------- */
  11. #endif
  12. int64_t size = 0; /* 64-bit */
  13. size_t len = sizeof(size);
  14. if (sysctl( mib, 2, &size, &len, NULL, 0) == 0)
  15. return (size_t)size;
  16. return 0L; /* Failed? */
  17. #elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE)
  18. /* FreeBSD, Linux, OpenBSD, and Solaris. -------------------- */
  19. return (size_t)sysconf(_SC_PHYS_PAGES) * (size_t)sysconf(_SC_PAGESIZE);
  20. #elif defined(CTL_HW) && (defined(HW_PHYSMEM) || defined(HW_REALMEM))
  21. /* DragonFly BSD, FreeBSD, NetBSD, OpenBSD, and OSX. -------- */
  22. int mib[2];
  23. mib[0] = CTL_HW;
  24. #if defined(HW_REALMEM)
  25. mib[1] = HW_REALMEM; /* FreeBSD. ----------------- */
  26. #elif defined(HW_PHYSMEM)
  27. mib[1] = HW_PHYSMEM; /* Others. ------------------ */
  28. #endif
  29. unsigned int size = 0; /* 32-bit */
  30. size_t len = sizeof(size);
  31. if (sysctl(mib, 2, &size, &len, NULL, 0) == 0)
  32. return (size_t)size;
  33. return 0L; /* Failed? */
  34. #else
  35. return 0L; /* Unknown method to get the data. */
  36. #endif
  37. #else
  38. return 0L; /* Unknown OS. */
  39. #endif
  40. }

获取物理内存的字节数

总结

看了redis内存分配的源码后,其实没有相信中的那么难以理解,或许只是心理上的作用,当然也说明redis源码写得真的是好,让我这种渣渣都能轻而易举的看懂,并且注释也很少,这里的函数几乎都是对glibc的malloc中的函数进行了一层包装,并且维护了一个叫做used_memory的全局变量,并且每一次对全局变量的操作都是原子操作。也对一些常用的函数进行了封装,例如:获取rss的大小,获取/proc/pid/smaps文件中某一field占用字节数的大小,获取物理内存字节数等等,总的来说,收益匪浅,没想到内存操作可以做到这样简单。

redis源码解读--内存分配zmalloc的更多相关文章

  1. redis源码笔记-内存管理zmalloc.c

    redis的内存分配主要就是对malloc和free进行了一层简单的封装.具体的实现在zmalloc.h和zmalloc.c中.本文将对redis的内存管理相关几个比较重要的函数做逐一的介绍 参考: ...

  2. 鸿蒙内核源码分析(内存分配篇) | 内存有哪些分配方式  | 百篇博客分析OpenHarmony源码 | v11.02

    百篇博客系列篇.本篇为: v11.xx 鸿蒙内核源码分析(内存分配篇) | 内存有哪些分配方式 | 51.c.h .o 内存管理相关篇为: v11.xx 鸿蒙内核源码分析(内存分配篇) | 内存有哪些 ...

  3. redis笔记_源码_内存分配

    文件:zmoalloc.h zmoalloc.c 1.求两个整数的余数 eg: 求_n对sizeof(long)的余数(_n&(sizeof(long)-1)), 性能提升为50%-100% ...

  4. (十)redis源码解读

    一.redis工作机制 redis是 单线程,所有命令(set,get等)都会加入到队列中,然后一个个执行. 二.为什么redis速度快? 1.基于内存 2.redis协议resp 简单.可读.效率高 ...

  5. 鸿蒙内核源码分析(内存规则篇) | 内存管理到底在管什么 | 百篇博客分析OpenHarmony源码 | v16.02

    百篇博客系列篇.本篇为: v16.xx 鸿蒙内核源码分析(内存规则篇) | 内存管理到底在管什么 | 51.c.h .o 内存管理相关篇为: v11.xx 鸿蒙内核源码分析(内存分配篇) | 内存有哪 ...

  6. 鸿蒙内核源码分析(内存映射篇) | 虚拟内存虚在哪里 | 百篇博客分析OpenHarmony源码 | v15.03

    百篇博客系列篇.本篇为: v15.xx 鸿蒙内核源码分析(内存映射篇) | 虚拟内存虚在哪里 | 51.c.h .o 内存管理相关篇为: v11.xx 鸿蒙内核源码分析(内存分配篇) | 内存有哪些分 ...

  7. 鸿蒙内核源码分析(内存汇编篇) | 谁是虚拟内存实现的基础 | 百篇博客分析OpenHarmony源码 | v14.14

    百篇博客系列篇.本篇为: v14.xx 鸿蒙内核源码分析(内存汇编篇) | 谁是虚拟内存实现的基础 | 51.c.h .o 内存管理相关篇为: v11.xx 鸿蒙内核源码分析(内存分配篇) | 内存有 ...

  8. 鸿蒙内核源码分析(内存管理篇) | 虚拟内存全景图是怎样的 | 百篇博客分析OpenHarmony源码 | v12.04

    百篇博客系列篇.本篇为: v12.xx 鸿蒙内核源码分析(内存管理篇) | 虚拟内存全景图是怎样的 | 51.c.h .o 内存管理相关篇为: v11.xx 鸿蒙内核源码分析(内存分配篇) | 内存有 ...

  9. Redis源码阅读(四)集群-请求分配

    Redis源码阅读(四)集群-请求分配 集群搭建好之后,用户发送的命令请求可以被分配到不同的节点去处理.那Redis对命令请求分配的依据是什么?如果节点数量有变动,命令又是如何重新分配的,重分配的过程 ...

随机推荐

  1. OpenFOAM中的基本变量快速认知【转载】

    转载自:http://blog.sina.com.cn/s/blog_a0b4201d0102vsf9.html label 实际上就是整型数据的变体,int,OF对它进行了包装,以适应32或64位系 ...

  2. mysql8安装与卸载

    参考: https://www.cnblogs.com/zxwen/p/9448797.html https://blog.csdn.net/weixin_30073553/article/detai ...

  3. Leetcode题目461:汉明距离(位运算-简单)

    题目描述: 两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目. 给出两个整数 x 和 y,计算它们之间的汉明距离. 注意:0 ≤ x, y < 231. 示例: 输入: x = ...

  4. html 获取地址栏信息

    <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8& ...

  5. Cesium获取经度 ,纬度,高度

    实例代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF- ...

  6. golang通过ssh实现远程文件传输

    使用ssh远程操作文件, 主要是创建ssh, 直接上代码 import ( "fmt" "github.com/pkg/sftp" "golang.o ...

  7. php - thinkphp3.2-phpQrcode生成二维码

    import('/Doctor.Logic.phpqrcode',APP_PATH,'.php');// import('@.Doctor.Logic');$value = 'http://www.c ...

  8. React拾遗(上)

    JSX代表Objects Babel转义器会把JSX转换成一个名为React.createElement()的方法调用. 下面两种代码的作用是完全相同的: const element = ( < ...

  9. <JavaScript>调用apply报错:CreateListFromArrayLike called on non-object;

    Function.apply(obj, args)方法能接收两个参数 obj:这个对象将代替Function类里this对象 args:这个是数组,它将作为参数传给Function(args--> ...

  10. mac kafka 环境搭建 以及PHP的kafka扩展

    1.kafka安装 brew install kafka 安装会依赖zookeeper. 注意:安装目录:/usr/local/Cellar/kafka/0.10.2.0 2.安装的配置文件位置 /u ...