上一遍详细的写明了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版本,或者安装两个成熟的内存管理库来提高性能。

最新文章

  1. Microsoft Azure Web Sites应用与实践【3】—— 通过Visual Studio Online在线编辑Microsoft Azure 网站
  2. 关于C# DataTable 的一些操作
  3. jquery 回到 顶部
  4. C#文件和文件文件夹按时间、名称排序-顺序与倒序
  5. 如何判断CPU字节序之[Big-endian vs Little-endian]
  6. js 数组去重复键
  7. Python学习之路——初识Python
  8. hdu Oulipo(kmp)
  9. href 做导航 特效
  10. Solr安装(Tomcat)
  11. FRP 浅析
  12. Phpcms 前台页面实现分页
  13. DC/OS安装
  14. java 简单程序
  15. 使用Jmeter监测服务器性能指标
  16. 微信小程序使用wxParse,解决图片显示路径问题
  17. redis5.0.0.版设置开机自启
  18. 【codeforces contest 1119 F】Niyaz and Small Degrees
  19. 由设置body线性背景色引发的问题-----当声明文档类型时,对body设置线性背景色,页面背景色无法整体线性过渡
  20. ASP.Net MVC(1) 之走进MVC

热门文章

  1. HNU 12906 Battleship
  2. NSBundle/其他Bundle的获取
  3. phpstorm的调试工具xdebug
  4. mysql连表更新
  5. 回归基础: JavaScript 变量提升
  6. cocos2d智能指针 转自:http://blog.csdn.net/nxshow/article/details/44699409
  7. 剑指Offer 跳台阶
  8. 【工具】【版本控制】TortoiseSVN过滤文件与文件夹
  9. dtw算法
  10. phpcms不显示验证码