主要函数

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

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
}

参数size是需要分配的空间大小。事实上我们需要分配的空间大小为size+PREFIX_SIZE。PREFIX_SIZE是根据平台的不同和HAVE_MALLOC_SIZE宏定义控制的。如果malloc()函数调用失败,就会调用zmalloc_oom_handler()函数来打印异常,并且会终止函数,zmalloc_oom_handler其实是一个函数指针,真正调用的函数是zmalloc_default_oom(),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();
} static void (*zmalloc_oom_handler)(size_t) = zmalloc_default_oom;

内存分配成功之后,会依据HAVE_MALLOC_SIZE的控制对前八个字节操作,用以记录分配内存的长度,会在update_zmalloc_stat_alloc()宏定义函数中更新used_memory这个静态变量的值,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)
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)

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函数和zmalloc函数处理的思路很是相似,就不做太多的解释了

void *zrealloc(void *ptr, size_t size)

void *zrealloc(void *ptr, size_t size) {
// 如果没有定义HAVE_MALLOC_SIZE,就说明PREFIX_SIZE宏定义不为0,那么ptr并不是该段内存真正的开始地址
#ifndef HAVE_MALLOC_SIZE
void *realptr;
#endif
size_t oldsize;
void *newptr; if (ptr == NULL) return zmalloc(size);
// 根据HAVE_MALLOC_SIZE宏定义,oldsize,newptr获取方式不一样,以及更新used_memory的细节
#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
}

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

void zfree(void *ptr)

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函数和zrealloc函数做法差不到太多,都是对oldsize和realptr对HAVE_MALLOC_SIZE有无声明分别进行操作。

char *zstrdup(const char *s)

char *zstrdup(const char *s) {
size_t l = strlen(s)+1;
char *p = zmalloc(l); memcpy(p,s,l);
return p;
}

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

size_t zmalloc_used_memory(void)

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

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

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

void zmalloc_set_oom_handler(void (*oom_handler)(size_t)) {
zmalloc_oom_handler = oom_handler;
}

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

size_t zmalloc_get_rss(void)

size_t zmalloc_get_rss(void) {
int page = sysconf(_SC_PAGESIZE);
size_t rss;
char buf[4096];
char filename[256];
int fd, count;
char *p, *x; snprintf(filename,256,"/proc/%d/stat",getpid());
if ((fd = open(filename,O_RDONLY)) == -1) return 0;
if (read(fd,buf,4096) <= 0) {
close(fd);
return 0;
}
close(fd); p = buf;
count = 23; /* RSS is the 24th field in /proc/<pid>/stat */
while(p && count--) {
p = strchr(p,' ');
if (p) p++;
}
if (!p) return 0;
x = strchr(p,' ');
if (!x) return 0;
*x = '\0'; rss = strtoll(p,NULL,10);
rss *= page;
return rss;
}

返回驻留集大小

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

#if defined(USE_JEMALLOC)
int zmalloc_get_allocator_info(size_t *allocated,
size_t *active,
size_t *resident) {
uint64_t epoch = 1;
size_t sz;
*allocated = *resident = *active = 0;
/* Update the statistics cached by mallctl. */
sz = sizeof(epoch);
je_mallctl("epoch", &epoch, &sz, &epoch, sz);
sz = sizeof(size_t);
/* Unlike RSS, this does not include RSS from shared libraries and other non
* heap mappings. */
je_mallctl("stats.resident", resident, &sz, NULL, 0);
/* Unlike resident, this doesn't not include the pages jemalloc reserves
* for re-use (purge will clean that). */
je_mallctl("stats.active", active, &sz, NULL, 0);
/* Unlike zmalloc_used_memory, this matches the stats.resident by taking
* into account all allocations done by this process (not only zmalloc). */
je_mallctl("stats.allocated", allocated, &sz, NULL, 0);
return 1;
}
#else
int zmalloc_get_allocator_info(size_t *allocated,
size_t *active,
size_t *resident) {
*allocated = *resident = *active = 0;
return 1;
}
#endif

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

size_t zmalloc_get_smap_bytes_by_field(char *field, long pid)

#if defined(HAVE_PROC_SMAPS)
size_t zmalloc_get_smap_bytes_by_field(char *field, long pid) {
char line[1024];
size_t bytes = 0;
int flen = strlen(field);
FILE *fp; if (pid == -1) {
// /proc/pid/smaps反应了运行时的进程的内存影响,系统的运行时库(so),堆,栈信息均可在其中看到。
fp = fopen("/proc/self/smaps","r");
} else {
char filename[128];
snprintf(filename,sizeof(filename),"/proc/%ld/smaps",pid);
fp = fopen(filename,"r");
} if (!fp) return 0;
while(fgets(line,sizeof(line),fp) != NULL) {
if (strncmp(line,field,flen) == 0) {
char *p = strchr(line,'k');
if (p) {
*p = '\0';
bytes += strtol(line+flen,NULL,10) * 1024;
}
}
}
fclose(fp);
return bytes;
}
#else
size_t zmalloc_get_smap_bytes_by_field(char *field, long pid) {
((void) field);
((void) pid);
return 0;
}
#endif

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

size_t zmalloc_get_private_dirty(long pid)

size_t zmalloc_get_private_dirty(long pid) {
return zmalloc_get_smap_bytes_by_field("Private_Dirty:",pid);
}

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

size_t zmalloc_get_memory_size(void)

size_t zmalloc_get_memory_size(void) {
#if defined(__unix__) || defined(__unix) || defined(unix) || \
(defined(__APPLE__) && defined(__MACH__))
#if defined(CTL_HW) && (defined(HW_MEMSIZE) || defined(HW_PHYSMEM64))
int mib[2];
mib[0] = CTL_HW;
#if defined(HW_MEMSIZE)
mib[1] = HW_MEMSIZE; /* OSX. --------------------- */
#elif defined(HW_PHYSMEM64)
mib[1] = HW_PHYSMEM64; /* NetBSD, OpenBSD. --------- */
#endif
int64_t size = 0; /* 64-bit */
size_t len = sizeof(size);
if (sysctl( mib, 2, &size, &len, NULL, 0) == 0)
return (size_t)size;
return 0L; /* Failed? */ #elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE)
/* FreeBSD, Linux, OpenBSD, and Solaris. -------------------- */
return (size_t)sysconf(_SC_PHYS_PAGES) * (size_t)sysconf(_SC_PAGESIZE); #elif defined(CTL_HW) && (defined(HW_PHYSMEM) || defined(HW_REALMEM))
/* DragonFly BSD, FreeBSD, NetBSD, OpenBSD, and OSX. -------- */
int mib[2];
mib[0] = CTL_HW;
#if defined(HW_REALMEM)
mib[1] = HW_REALMEM; /* FreeBSD. ----------------- */
#elif defined(HW_PHYSMEM)
mib[1] = HW_PHYSMEM; /* Others. ------------------ */
#endif
unsigned int size = 0; /* 32-bit */
size_t len = sizeof(size);
if (sysctl(mib, 2, &size, &len, NULL, 0) == 0)
return (size_t)size;
return 0L; /* Failed? */
#else
return 0L; /* Unknown method to get the data. */
#endif
#else
return 0L; /* Unknown OS. */
#endif
}

获取物理内存的字节数

总结

看了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. [WEB安全]phpMyadmin后台任意文件包含漏洞分析(CVE-2018-12613)

    0x00 简介 影响版本:4.8.0--4.8.1 本次实验采用版本:4.8.1 0x01 效果展示 payload: http://your-ip:8080/index.php?target=db_ ...

  2. Arts打卡第8周

    Algorithm.主要是为了编程训练和学习. 每周至少做一个 leetcode 的算法题(先从Easy开始,然后再Medium,最后才Hard). 进行编程训练,如果不训练你看再多的算法书,你依然不 ...

  3. 文件CRC和MD5校验

    文件CRC和MD5校验 CRC和MD5用于文件和数据的传输校验,以确认是否接收成功. unit CRCMD5; interface { 获取文件CRC校验码 } function GetFileCRC ...

  4. SQLite3中的日期时间函数使用小结

    代码如下: import sqlite3conn = sqlite3.connect('/tmp/sqlite.db')cur = conn.cursor() 接下来干嘛呢?建一张表吧.这里需要注意的 ...

  5. Windows 下 把EXE 程序变成服务运行

    1. 下载  instsrv.exe 和 srvany.exe   我下载的地址  [点击打开链接][https://www.cr173.com/soft/64394.html] 2. cmd  cd ...

  6. openrstry 限流 是否有清零逻辑

    openrstry  限流  是否有清零逻辑 https://github.com/openresty/lua-resty-limit-traffic

  7. redis json 降低性能 使用 hash

    使用hashtable和hash-max-zipmap-entries内存优化和效率http://www.flyfifi.cn/blog/detail/71/

  8. Bitmap之compress图片压缩

    package com.loaderman.customviewdemo; import android.app.Activity; import android.graphics.Bitmap; i ...

  9. NetHogs监控Linux的每个进程流量

    在日常运维环境中,我们肯定会遇到以下这种需求: 1.网络流量异常,不知道是哪个程序的流量爆涨? 2.日常需要监控网络实时的流量进去数据 面试装逼系列|这篇文章,让运维监控不再成为你的短板! 学会这 1 ...

  10. Unix/Linux系统下获得时间戳函数

    在Unix/Linux系统下,使用gettimeofday函数来获得当前系统的时间戳,精度可达到微秒(microsecond,即μs)级别. 通过结构体timeval来存放当前时间戳的信息: #ifn ...