本文章主要记录本人在看redis源代码的一些理解和想法。由于功力有限,肯定会出现故障,所以。希望高手给出指正。
第一篇就是内存相关的介绍。由于我喜欢先看一些组件的东西,再看总体的流程。
先上一下代码吧
头文件
//主要提供内存分配和释放的基础功能
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_enable_thread_safeness(void); void zmalloc_set_oom_handler(void (*oom_handler)(size_t)); float zmalloc_get_fragmentation_ratio(void); size_t zmalloc_get_rss(void); size_t zmalloc_get_private_dirty(void); void zlibc_free(void *ptr); #ifndef HAVE_MALLOC_SIZE size_t zmalloc_size(void *ptr); #endif
0.前言
在这块代码我们能够看到HAVE_ATOMIC 宏定义,有什么作用呢。
该文件主要提供了tcmalloc 和jemalloc内存的管理。
tcmalloc是google perftool的一部分。与一般的内存池不同,它直接与os打交道,内存闲置时os会进行回收(stl内存池就不回收),同一时候使用TLS(Thread
local storage)管理内存池,避免一个线程内分配内存都要同步。
jemalloc与tcmalloc相似,作者Jason
Evans是Free BSD开发者,性能与使用率与tcmalloc不相伯仲。
tcmalloc更方便与google perftool集成,进行性能评測。
zmalloc主要是 提供了对malloc函数的封装,假设是glibc的malloc函数,那么分配的内存是长度+要分配的的内存。然后将头部放入大小
1.申请内存
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 }
update_zmalloc_stat_alloc(zmalloc_size(ptr));是先将自己内存对齐,假设long是4位就对齐到4的整数倍。然后将内存的大小记录下来到一个全局变量中
例如以下代码
#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)
内存对其有非常多种方式,他这样的也是非常有意思。
使用long就能够无论是64机器还是32位机器了。假设是在多线程情况下是使用了相互排斥锁。
假设没定义宏HAVE_MALLOC_SIZE,就在内存的前边四个字节存储器大小。最后返回内存的起始地址。
同理
void *zcalloc(size_t size)
该方法封装了calloc 函数功能。
2.又一次申请内存
代码例如以下
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 }
该方法就是对指定的一段内存又一次申请指定大小的内存。地址没变化。
3.释放内存
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 }
4.拷贝内存,封装了memcpy函数
char *zstrdup(const char *s) { size_t l = strlen(s)+1; char *p = zmalloc(l); memcpy(p,s,l); return p; }
5获取RSS 物理内存值
通过查看进程的/proc/pid/stat 文件的第23行。
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; }
6.内存使用率
/* Fragmentation = RSS / allocated-bytes */ float zmalloc_get_fragmentation_ratio(void) { return (float)zmalloc_get_rss()/zmalloc_used_memory(); }
7.多线程下的原子添加
#ifdef HAVE_ATOMIC #define update_zmalloc_stat_add(__n) __sync_add_and_fetch(&used_memory, (__n)) #define update_zmalloc_stat_sub(__n) __sync_sub_and_fetch(&used_memory, (__n)) #else #define update_zmalloc_stat_add(__n) do { pthread_mutex_lock(&used_memory_mutex); used_memory += (__n); pthread_mutex_unlock(&used_memory_mutex); } while(0) #define update_zmalloc_stat_sub(__n) do { pthread_mutex_lock(&used_memory_mutex); used_memory -= (__n); pthread_mutex_unlock(&used_memory_mutex); } while(0) #endif
redis这块内存使用谷歌和jemalloc 进行的内存管理是很高效的。
对外提供的接口也比較简洁。
值得学习。
很多其它文章。欢迎关注: