上一遍详细的写明了Redis为内存管理所做的初始化工作,这篇文章写具体的函数实现。

1、zmalloc_size,返回内存池大小函数,因为库不同,所以这个函数在内部有很多的宏定义,通过具体使用的库来确定到底用哪个。

#define zmalloc_size(p) tc_malloc_size(p)//TCMalloc
#define zmalloc_size(p) je_malloc_usable_size(p)//Jemalloc
#define zmalloc_size(p) malloc_size(p)//Mac
//使用系统库的情况下,要单独写返回函数
#ifndef HAVE_MALLOC_SIZE
size_t zmalloc_size(void *ptr)
{
void *realptr = (char*)ptr-PREFIX_SIZE;
size_t size = *((size_t*)realptr);
//具体的计算还没看懂。
if (size&(sizeof(long)-1))
    size += sizeof(long)-(size&(sizeof(long)-1));
return size+PREFIX_SIZE;
}
#endif

2、zmalloc,内存分配函数

void *zmalloc(size_t size)
{/*普通的申请内存大小函数,申请的时候,内存的头部加上一个longlong长度的头部*/
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
}

3.zcalloc

/*重定义calloc函数,单每次只能申请一块内存,放弃了申请多块内存的功能,实际功能和zmalloc类似*/
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
}

4、zrealloc,该函数放弃了按内存块大小成倍申请的功能,最后功能类似zmalloc

 1 void *zrealloc(void *ptr, size_t size)
2 {
3 #ifndef HAVE_MALLOC_SIZE
4 //使用成熟库函数的情况下直接修改大小就好。最后更新使用内存量的大小。
5 void *realptr;
6 #endif
7
8 size_t oldsize;
9 void *newptr;
10
11 if (ptr == NULL) return zmalloc(size);
12 #ifdef HAVE_MALLOC_SIZE
13 oldsize = zmalloc_size(ptr);
14 newptr = realloc(ptr,size);
15 if (!newptr) zmalloc_oom_handler(size);
16
17 update_zmalloc_stat_free(oldsize);
18 update_zmalloc_stat_alloc(zmalloc_size(newptr));
19 return newptr;
20 #else
21 /*没有使用现成库的情况下,扩展内存
22 将指针前移,找到真正开始的头部并记录下真正的开始,
23 按照新的大小重新申请内存,然后释放旧的,最后更新使用内存量的大小。
24 */
25 realptr = (char*)ptr-PREFIX_SIZE;
26 oldsize = *((size_t*)realptr);
27 newptr = realloc(realptr,size+PREFIX_SIZE);
28 if (!newptr) zmalloc_oom_handler(size);
29
30 *((size_t*)newptr) = size;
31 update_zmalloc_stat_free(oldsize);
32 update_zmalloc_stat_alloc(size);
33 return (char*)newptr+PREFIX_SIZE;
34 #endif
35 }

5、zfree,内存释放函数

 1 void zfree(void *ptr) {
//这个也是分两部分,如果使用了成熟库,直接调用释放就好,如果使用系统的,还要找到真的地址开始的头部然后再释放,最后更新内存使用量的大小。
2 #ifndef HAVE_MALLOC_SIZE
3 void *realptr;
4 size_t oldsize;
5 #endif
6
7 if (ptr == NULL) return;
8 #ifdef HAVE_MALLOC_SIZE
9 update_zmalloc_stat_free(zmalloc_size(ptr));
10 free(ptr);
11 #else
12 realptr = (char*)ptr-PREFIX_SIZE;
13 oldsize = *((size_t*)realptr);
14 update_zmalloc_stat_free(oldsize+PREFIX_SIZE);
15 free(realptr);
16 #endif
17 }

6、zstrdup

1 /*字符串复制函数,给一个指定的字符串在堆上分配内存,注意,这个地方只是给一个字符串分配了内存,并没有转换成系统内统一的sds字符串,强行按sds使用会出错。*/
2 char *zstrdup(const char *s) {
3 size_t l = strlen(s)+1;
4 char *p = zmalloc(l);
5
6 memcpy(p,s,l);
7 return p;
8 }

7、zmalloc_used_memory,获取已经使用的内存的大小

 1 size_t zmalloc_used_memory(void)
2 {
3 size_t um;
4   //如果是线程安全情况下,读取当前使用内存量时要加互斥锁,不然可能会出现并发问题
5 if (zmalloc_thread_safe)
6 {
7 #if defined(__ATOMIC_RELAXED) || defined(HAVE_ATOMIC)
8 um = update_zmalloc_stat_add(0);
9 #else
10 pthread_mutex_lock(&used_memory_mutex);
11 um = used_memory;
12 pthread_mutex_unlock(&used_memory_mutex);
13 #endif
14 }
15 else
16 {//如果没设定线程安全,直接读取
17 um = used_memory;
18 }
19
20 return um;
21 }

8、zmalloc_enable_thread_safeness,设置线程安全,多线程模式下,最好是设置了,系统好像没提供解除的函数,也就是说系统一但确定了模式将不能改变。

void zmalloc_enable_thread_safeness(void)
{
zmalloc_thread_safe = 1;
}

9、zmalloc_set_oom_handler,设置内存异常处理函数,如果不设置,系统有一个默认的。

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

10、zmalloc_get_rss,获取进程总的虚拟内存的大小

#if defined(HAVE_PROC_STAT)
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> /*计算使用内存的总值*/
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;
/*从/proc/PID/stat文件中读取Virtual memory size的大小,指的是虚拟内存的页数*/
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; /* stat中第24个值记录的是虚拟内存的页数*/
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;
}
#elif defined(HAVE_TASKINFO)
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <mach/task.h>
#include <mach/mach_init.h> size_t zmalloc_get_rss(void) {
task_t task = MACH_PORT_NULL;
struct task_basic_info t_info;
mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; if (task_for_pid(current_task(), getpid(), &task) != KERN_SUCCESS)
return 0;
task_info(task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count); return t_info.resident_size;
}
#else
size_t zmalloc_get_rss(void) {
/* If we can't get the RSS in an OS-specific way for this system just
* return the memory usage we estimated in zmalloc()..
*
* Fragmentation will appear to be always 1 (no fragmentation)
* of course... */
return zmalloc_used_memory();
}
#endif

11、zmalloc_get_fragmentation_ratio,计算某块内存占已经使用的内存的百分比。

float zmalloc_get_fragmentation_ratio(size_t rss)
{
return (float)rss/zmalloc_used_memory();
}

12、最后两个函数不知道干嘛用的,所以没分析

#if defined(HAVE_PROC_SMAPS)
size_t zmalloc_get_smap_bytes_by_field(char *field)
{
char line[1024];
size_t bytes = 0;
FILE *fp = fopen("/proc/self/smaps","r");
int flen = strlen(field); 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)
{
((void) field);
return 0;
}
#endif size_t zmalloc_get_private_dirty(void) {
return zmalloc_get_smap_bytes_by_field("Private_Dirty:");
}

内存管理模块基本上分析完了,最后两个函数不知道是干嘛的等回头用的时候再说,总的来看,内存管理不是很麻烦,只给内存块加了一个头部来记录快大小,模块也充分考虑了性能,优先使用成熟的内存管理池,如果没有的话就优先使用编译器支持的原子操作,最后再都不支持的情况下才会使用互斥锁,如果在Redis使用的过程中互斥锁降低了性能,可以考虑升级gcc版本,或者安装两个成熟的内存管理库来提高性能。

Redis内存管理(二)的更多相关文章

  1. Redis 内存管理与事件处理

    1 Redis内存管理 Redis内存管理相关文件为zmalloc.c/zmalloc.h,其只是对C中内存管理函数做了简单的封装,屏蔽了底层平台的差异,并增加了内存使用情况统计的功能. void * ...

  2. Redis 内存管理 源码分析

    要想了解redis底层的内存管理是如何进行的,直接看源码绝对是一个很好的选择 下面是我添加了详细注释的源码,需要注意的是,为了便于源码分析,我把redis为了弥补平台差异的那部分代码删了,只需要知道有 ...

  3. Redis内存管理的基石zmallc.c源代码解读(一)

    当我第一次阅读了这个文件的源代码的时候.我笑了,忽然想起前几周阿里电话二面的时候,问到了自己定义内存管理函数并处理8字节对齐问题. 当时无言以对,在面试官无数次的提示下才答了出来,结果显而易见,挂掉了 ...

  4. 详解 Redis 内存管理机制和实现

    Redis是一个基于内存的键值数据库,其内存管理是非常重要的.本文内存管理的内容包括:过期键的懒性删除和过期删除以及内存溢出控制策略. 最大内存限制 Redis使用 maxmemory 参数限制最大可 ...

  5. Redis内存管理(一)

    Redis数据库的内存管理函数有关的文件为:zmalloc.h和zmalloc.c. Redis作者在编写内存管理模块时考虑到了查看系统内是否安装了TCMalloc或者Jemalloc模块,这两个是已 ...

  6. TCMalloc优化MySQL、Nginx、Redis内存管理

    TCMalloc(Thread-Caching Malloc)与标准glibc库的malloc实现一样的功能,但是TCMalloc在效率和速度效率都比标准malloc高很多.TCMalloc是 goo ...

  7. redis内存管理

    Redis主要通过控制内存上线和回收策略来实现内存管理. 1. 设置内存上限 redis使用maxmemory参数限制最大可用内存.限制的目的主要有: 用户缓存场景,当超出内存上限maxmemory时 ...

  8. JVM内存管理(二)

    JVM内存管理          JVM在执行java程序的过程中,会把内存划分为若干个不同的数据区域.这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则依赖 ...

  9. OC的内存管理(二)ARC

    指针: 指向内存的地址指针变量 存放地址的变量指针变量值 变量中存放的值(地址值)指针变量指向的内存单元值 内存地址指向的值1):强指针:默认的情况下,所有的指针都是强指针,关键字strong ):弱 ...

随机推荐

  1. jQuery框架分析第一章: 第一个匿名函数

    我的jQuery版本为1.7* 这个版本代码比之前的版本优化了很多,结构也清晰了不少,就用最新的吧. 打开jQuery源代码 首先你能看到所有代码被一个 (function(window,undefi ...

  2. Swift2.1 语法指南——错误处理

    原档:https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programmi ...

  3. MySQL监控系统MySQL MTOP的搭建(转VIII)

    MySQLMTOP是一个由Python+PHP开发的MySQL企业级监控系统.系统由Python实现多进程数据采集和告警,PHP实现WEB展示和管理.最重要是MySQL服务器无需安装任何Agent,只 ...

  4. mysql 按距离今日时间最近排序

    xxx order by abs(DATEDIFF(time_start,now()))

  5. sqlserver2008清日志

    use [DB Name] Select NAME,size From sys.database_files GO ALTER DATABASE [DB Name] SET RECOVERY SIMP ...

  6. Retina视网膜屏中CSS3边框图片像素虚边的问题

    虽然CSS3新增了这个功能,但是在W3school里面并没有给出具体详细的解释,还好网上不乏大神给你我们很全面的解释其中的原理-css3:border-image边框图像详解 边框图片的原理是四个角不 ...

  7. JavaScript中var关键字的使用详解

    作用 声明作用:如声明个变量. 语法 ? 1 var c = 1; 省略var 在javascript中,若省略var关键字而直接赋值,那么这个变量为全局变量,哪怕是在function里定义的. ? ...

  8. 2016年11月17日--SQL主、外键,子查询

    主键 数据库主键是指表中一个列或列的组合,其值能唯一地标识表中的每一行.这样的一列或多列称为表的主键,通过它可强制表的实体完整性.当创建或更改表时可通过定义 PRIMARY KEY约束来创建主键.一个 ...

  9. POJ 1509 Glass Beads

    Description 求字符串的最小循环表示. Sol SAM. 把原串复制一遍,建出SAM,然后每次选最小的一个跑 \(len\) 次,这就是最小循环表示的最后一个节点,然后 \(x-len+1\ ...

  10. flask 链接 url_for()

    通常html的文件都放在template里面,那么静态的文件放在哪呢?staitc里面 调用 url_for('static', filename='css/styles.css', _externa ...