標籤:
本文章主要記錄本人在看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_SIZEsize_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這塊記憶體使用量Google和jemalloc 進行的記憶體管理是很高效的。
對外提供的介面也比較簡潔。
值得學習。
很多其它文章。歡迎關註:
redis原始碼解讀之記憶體管理————zmalloc檔案