Redis内存管理的基石zmallc.c源代码解读(一)
当我第一次阅读了这个文件的源代码的时候。我笑了,忽然想起前几周阿里电话二面的时候,问到了自己定义内存管理函数并处理8字节对齐问题。
当时无言以对,在面试官无数次的提示下才答了出来,结果显而易见,挂掉了二面。而这份源代码中函数zmalloc()和zfree()的设计思路和实现原理,正是面试官想要的答案。
源代码结构
zmalloc.c文件的内容例如以下:
主要函数
- zmalloc()
- zfree()
- zcalloc()
- zrelloc()
- zstrdup()
字长与字节对齐
所谓的8字节对齐,就是指变量的起始地址是8的倍数。比方程序运行时(CPU)在读取long型数据的时候。仅仅须要一个总线周期,时间更短。假设不是8字节对齐的则须要两个总线周期才干读完数据。
里面多用sizeof(long)或sizeof(size_t)来表示。size_t(gcc中其值为long unsigned int)和long的长度是一样的,long的长度就是计算机的字长。
这样在未来的系统中假设字长(long的大小)不是8个字节了。该段代码依旧能保证对应代码可用。
zmalloc
- malloc()
- zmalloc_oom_handler【函数指针】
- zmalloc_default_oom()【被上面的函数指针所指向】
- update_zmalloc_stat_alloc()【宏函数】
- update_zmalloc_stat_add()【宏函数】
zmalloc()源代码
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是我们须要分配的内存大小。实际上我们调用malloc实际分配的大小是size+PREFIX_SIZE。PREFIX_SIZE是一个条件编译的宏。不同的平台有不同的结果,在Linux中其值是sizeof(size_t),所以我们多分配了一个字长(8个字节)的空间(后面代码可以看到多分配8个字节的目的是用于储存size的值)。
该函数实际上是一个函数指针指向函数zmalloc_default_oom,其主要功能就是打印错误信息并终止程序。
// oom是out of memory(内存不足)的意思
static void zmalloc_default_oom(size_t size) {
fprintf(stderr, "zmalloc: Out of memory trying to allocate %zu bytes\n",
size);
fflush(stderr);
abort();
}
接下来是宏的条件编译,我们聚焦在#else的部分。
*((size_t*)ptr) = size;
update_zmalloc_stat_alloc(size+PREFIX_SIZE);
return (char*)ptr+PREFIX_SIZE;
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)); \
if (zmalloc_thread_safe) { \
update_zmalloc_stat_add(_n); \
} else { \
used_memory += _n; \
} \
} while(0)
这个宏函数最外圈有一个do{...}while(0)循环看似毫无意义,实际上大有深意。
这部分内容不是本文讨论的重点,这里不再赘述。详细请看网上的这篇文章http://www.spongeliu.com/415.html。
if(_n&7) _n += 8 - (_n&7);
这段代码就是推断分配的内存空间的大小是不是8的倍数。假设内存大小不是8的倍数,就加上对应的偏移量使之变成8的倍数。_n&7 在功能上等价于 _n%8,只是位操作的效率显然更高。
used_memory是zmalloc.c文件里定义的全局静态变量,表示已分配内存的大小。假设是内存安全的就使用update_zmalloc_stat_add来给used_memory加上n。
#define update_zmalloc_stat_add(__n) do { \
pthread_mutex_lock(&used_memory_mutex); \
used_memory += (__n); \
pthread_mutex_unlock(&used_memory_mutex); \
} while(0)
pthread_mutex_lock()和pthread_mutex_unlock()使用相互排斥锁(mutex)来实现线程同步,前者表示加锁,后者表示解锁,它们是POSIX定义的线程同步函数。当加锁以后它后面的代码在多线程同一时候运行这段代码的时候就仅仅会运行一次,也就是实现了线程安全。
zfree
- free()
- update_zmalloc_free()【宏函数】
- update_zmalloc_sub()【宏函数】
- zmalloc_size()
zfree()源代码
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
}
重点关注#else后面的代码
realptr = (char *)ptr - PREFIX_SIZE;
oldsize = *((size_t*)realptr);
先进行类型转换再取指针所指向的值。通过zmalloc()函数的分析,可知这里存储着我们最初须要分配的内存大小(zmalloc中的size)。这里赋值个oldsize
update_zmalloc_stat_free(oldsize+PREFIX_SIZE);
update_zmalloc_stat_free()也是一个宏函数,和zmalloc中update_zmalloc_stat_alloc()大致同样。唯一不同之处是前者在给变量used_memory减去分配的空间,而后者是加上该空间大小。
最后free(realptr)。清除空间
update_zmalloc_free源代码
#define update_zmalloc_stat_free(__n) do { \
size_t _n = (__n); \
if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \
if (zmalloc_thread_safe) { \
update_zmalloc_stat_sub(_n); \
} else { \
used_memory -= _n; \
} \
} while(0)
当中的函数update_zmalloc_sub与zmalloc()中的update_zmalloc_add相对应,但功能相反,提供线程安全地used_memory减法操作。
#define update_zmalloc_stat_sub(__n) do { \
pthread_mutex_lock(&used_memory_mutex); \
used_memory -= (__n); \
pthread_mutex_unlock(&used_memory_mutex); \
} while(0)
zcalloc
void *calloc(size_t nmemb, size_t size);
void *zcalloc(size_t size);
- 它分配的空间大小是 size * nmemb。比方calloc(10,sizoef(char)); // 分配10个字节
- calloc()会对分配的空间做初始化工作(初始化为0),而malloc()不会
- calloc()
- update_zmalloc_stat_alloc()【宏函数】
- update_zmalloc_stat_add()【宏函数】
zcalloc()源代码
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()中没有calloc()的第一个函数nmemb。
由于它每次调用calloc(),其第一个參数都是1。
也就是说zcalloc()功能是每次分配 size+PREFIX_SIZE 的空间,并初始化。
zrealloc
void *realloc (void *ptr, size_t size);
void *zrealloc(void *ptr, size_t size);
- zmalloc()
- zmalloc_size()
- realloc()
- zmalloc_oom_handler【函数指针】
- update_zmalloc_stat_free()【宏函数】
- update_zmalloc_stat_alloc()【宏函数】
zrealloc()源代码
void *zrealloc(void *ptr, size_t size) {
#ifndef HAVE_MALLOC_SIZE
void *realptr;
#endif
size_t oldsize;
void *newptr; if (ptr == NULL) return zmalloc(size);
#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);
update_zmalloc_stat_alloc(size);
return (char*)newptr+PREFIX_SIZE;
#endif
}
经过前面关于zmalloc()和zfree()的源代码解读,相信您一定可以非常轻松地读懂zrealloc()的源代码,这里我就不赘述了。
zstrdup
从这个函数名中,非常easy发现它是string duplicate的缩写,即字符串复制。它的代码比較简单。先看一下声明:
char *zstrdup(const char *s);
功能描写叙述:复制字符串s的内容。到新的内存空间,构造新的字符串【堆区】。
并将这段新的字符串地址返回。
zstrdup源代码
char *zstrdup(const char *s) {
size_t l = strlen(s)+1;
char *p = zmalloc(l); memcpy(p,s,l);
return p;
}
- 首先,先获得字符串s的长度,新闻strlen()函数是不统计'\0'的,所以最后要加1。
- 然后调用zmalloc()来分配足够的空间。首地址为p。
- 调用memcpy来完毕复制。
- 然后返回p。
memcpy
声明例如以下:
void *memcpy(void *dest, const void *src, size_t n);
dest即目的地址。src是源地址。n是要复制的字节数。
Redis内存管理的基石zmallc.c源代码解读(一)的更多相关文章
- Redis 内存管理与事件处理
1 Redis内存管理 Redis内存管理相关文件为zmalloc.c/zmalloc.h,其只是对C中内存管理函数做了简单的封装,屏蔽了底层平台的差异,并增加了内存使用情况统计的功能. void * ...
- Redis 内存管理 源码分析
要想了解redis底层的内存管理是如何进行的,直接看源码绝对是一个很好的选择 下面是我添加了详细注释的源码,需要注意的是,为了便于源码分析,我把redis为了弥补平台差异的那部分代码删了,只需要知道有 ...
- Redis内存管理(一)
Redis数据库的内存管理函数有关的文件为:zmalloc.h和zmalloc.c. Redis作者在编写内存管理模块时考虑到了查看系统内是否安装了TCMalloc或者Jemalloc模块,这两个是已 ...
- Redis内存管理(二)
上一遍详细的写明了Redis为内存管理所做的初始化工作,这篇文章写具体的函数实现. 1.zmalloc_size,返回内存池大小函数,因为库不同,所以这个函数在内部有很多的宏定义,通过具体使用的库来确 ...
- TCMalloc优化MySQL、Nginx、Redis内存管理
TCMalloc(Thread-Caching Malloc)与标准glibc库的malloc实现一样的功能,但是TCMalloc在效率和速度效率都比标准malloc高很多.TCMalloc是 goo ...
- redis内存管理
Redis主要通过控制内存上线和回收策略来实现内存管理. 1. 设置内存上限 redis使用maxmemory参数限制最大可用内存.限制的目的主要有: 用户缓存场景,当超出内存上限maxmemory时 ...
- 详解 Redis 内存管理机制和实现
Redis是一个基于内存的键值数据库,其内存管理是非常重要的.本文内存管理的内容包括:过期键的懒性删除和过期删除以及内存溢出控制策略. 最大内存限制 Redis使用 maxmemory 参数限制最大可 ...
- redis内存管理代码的目光
zmalloc.h /* zmalloc - total amount of allocated memory aware version of malloc() * * Copyright (c) ...
- redis 内存管理与数据淘汰机制(转载)
原文地址:http://www.jianshu.com/p/2f14bc570563?from=jiantop.com 最大内存设置 默认情况下,在32位OS中,Redis最大使用3GB的内存,在64 ...
随机推荐
- 亚马逊AWS学习——EC2的自己定义VPC配置
转载请注明出处:http://blog.csdn.net/dongdong9223/article/details/47153421 本文出自[我是干勾鱼的博客] 1 网络配置 EC2即亚马逊AWS云 ...
- 阿里一道Java并发面试题 (详细分析篇)
说明 前天分享了一篇关于阿里的"Java常见疑惑和陷阱"的文章,有人说这个很早就有了,可能我才注意到,看完之后发现内容非常不错,有几个我也是需要停顿下想想,如果后续有机会我录制一个 ...
- localStorage存储数据位置
chrome浏览器:C:\Users\Username\AppData\Local\Google\Chrome\User Data\Default\Local Storage 中,虽然后缀名是.loc ...
- Appium_Python_API
1) find_element_by_android_uiautomator (‘new UiSelector().text(“XXXX”)’).click 正常匹配2) find_element_b ...
- Android Studio - no debuggable applications 的解决的方法
之前logcat总是无法显示调试应用的信息 曾经我都是卸载重装.后来发如今StackOverflow有一个哥们说的非常对.一次就成功. 原话是这么说的: You also should have To ...
- 软件——机器学习与Python,if __name__ == '__main__':函数
if __name__ == '__main__': 想必很多初次接触python都会见到这样一个语句,if __name__ == "__main__": 那么这个语句到底是做什 ...
- [Angular] Design API for show / hide components based on Auth
Simple Auth service: import { Injectable } from '@angular/core'; import {HttpClient} from '@angular/ ...
- UVA10006 - Carmichael Numbers(筛选构造素数表+高速幂)
UVA10006 - Carmichael Numbers(筛选构造素数表+高速幂) 题目链接 题目大意:假设有一个合数.然后它满足随意大于1小于n的整数a, 满足a^n%n = a;这种合数叫做Ca ...
- TTS-零基础入门之语音模板化
上篇介绍了TTS的一个简单样例http://blog.csdn.net/u010176014/article/details/47326413 本篇咱们进一步聊聊 语音怎样读模板. 比方 公交车上的模 ...
- 3dmax入门
动画 自己主动关键帧 设置关键帧 路径绑定 材质M打开 渲染f10 骨骼绑定. ..