標籤:
Redis資料庫的記憶體管理函數有關的檔案為:zmalloc.h和zmalloc.c。
Redis作者在編寫記憶體管理模組時考慮到了查看系統內是否安裝了TCMalloc或者Jemalloc模組,這兩個是已經存在很久的記憶體管理模組,代碼穩定、效能優異,如果已經安裝的話,則使用之,最後檢查是否是Mac系統,如果是Mac系統的話載入的檔案不同,額,本人沒進行過Mac編程,這塊兒不考慮。對應的原始碼為:
1 //檢查是否定義了TCMalloc,TCMalloc(Thread-Caching Malloc)與標準glibc庫的malloc實現一樣的功能,但是TCMalloc在效率和速度效率都比標準malloc高很多 2 #if defined(USE_TCMALLOC) 3 #define ZMALLOC_LIB ("tcmalloc-" __xstr(TC_VERSION_MAJOR) "." __xstr(TC_VERSION_MINOR)) 4 #include <google/tcmalloc.h> 5 6 #if (TC_VERSION_MAJOR == 1 && TC_VERSION_MINOR >= 6) || (TC_VERSION_MAJOR > 1) 7 #define HAVE_MALLOC_SIZE 1 8 #define zmalloc_size(p) tc_malloc_size(p) 9 #else10 #error "Newer version of tcmalloc required"11 #endif12 //檢查是否定義了jemalloc庫13 #elif defined(USE_JEMALLOC)14 #define ZMALLOC_LIB ("jemalloc-" __xstr(JEMALLOC_VERSION_MAJOR) "." __xstr(JEMALLOC_VERSION_MINOR) "." __xstr(JEMALLOC_VERSION_BUGFIX))15 #include <jemalloc/jemalloc.h>16 #if (JEMALLOC_VERSION_MAJOR == 2 && JEMALLOC_VERSION_MINOR >= 1) || (JEMALLOC_VERSION_MAJOR > 2)17 #define HAVE_MALLOC_SIZE 118 #define zmalloc_size(p) je_malloc_usable_size(p)19 #else20 #error "Newer version of jemalloc required"21 #endif22 //檢查是否是蘋果系統23 #elif defined(__APPLE__)24 #include <malloc/malloc.h>25 #define HAVE_MALLOC_SIZE 1/*標記已經找到了可用的現成函數庫*/26 #define zmalloc_size(p) malloc_size(p)/*因為每個庫的實現方式不一樣,所以測試大小的函數也不一樣*/27 #endif
先整體說一下記憶體管理的所有介面函數,然後慢慢分析,所有的介面函數為:
void *zmalloc(size_t size);//重寫malloc函數,申請記憶體void *zcalloc(size_t size);//重寫calloc函數,不再支援按塊的成倍申請,內部調用的是zmalloc
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); //設定記憶體管理為多安全執行緒模式,設定之後在更新已使用記憶體大小時會用Mutex進行互斥操作。
void zmalloc_set_oom_handler(void (*oom_handler)(size_t)); //設定記憶體異常時調用的函數。
float zmalloc_get_fragmentation_ratio(size_t rss); //
size_t zmalloc_get_rss(void); //擷取進程可使用的所有記憶體大小。
size_t zmalloc_get_private_dirty(void); //
size_t zmalloc_get_smap_bytes_by_field(char *field);
void zlibc_free(void *ptr); //釋放指標指向記憶體函數,用這個釋放記憶體時,不會更新使用記憶體變數的值。
#ifndef HAVE_MALLOC_SIZE
size_t zmalloc_size(void *ptr); //擷取記憶體塊總題大小函數。
#endif
在解析函數的詳細實現前,先分析一下.c檔案開始所做的處理。在.c檔案中定義了三個變數,一個用來記錄已經使用記憶體的大小,一個用來記錄是否開啟了多安全執行緒模式,一個是互斥鎖。
static size_t used_memory = 0;/*記錄已經使用碓記憶體的大小*/static int zmalloc_thread_safe = 0;/*是否開啟了安全執行緒*/pthread_mutex_t used_memory_mutex = PTHREAD_MUTEX_INITIALIZER;/*互斥鎖,如果開啟了多安全執行緒,而編譯器又不支援原子操作的函數,則需要用互斥鎖來完成代碼的互斥操作。*/
接下來要確定記憶體塊前面是否需要加個頭部,因為不能確定具體使用的到底是什麼記憶體配置函數,TCMalloc或者Jemalloc庫在申請的記憶體塊前增加了一個小塊的記憶體來記錄該記憶體塊的使用方式,因為本人沒分析過著倆庫,具體的不做分析,具體的代碼為:
#ifdef HAVE_MALLOC_SIZE//找到了已有的記憶體管理庫時就會定義這個宏,如果已經定義了,則不再增加頭部大小,如果沒有定義,則根據具體的系統來確定增加頭部大小的長度。 #define PREFIX_SIZE (0)//頭部長度為0#else #if defined(__sun) || defined(__sparc) || defined(__sparc__) #define PREFIX_SIZE (sizeof(long long))//頭部長度為longlong的大小 #else #define PREFIX_SIZE (sizeof(size_t))//頭部長度為size_tde的大小 #endif#endif
接下來是定義了兩個更新記憶體佔用變數的操作函數update_zmalloc_stat_add和update_zmalloc_stat_sub,這倆函數支援多線程。
1 /*先檢查編譯器是否支援原子操作函數,如果支援的話,就不用互斥鎖了,畢竟鎖的效率很低。*/ 2 #if defined(__ATOMIC_RELAXED)//這個好像是C++11標準開始支援的,具體的不是很清楚 3 #define update_zmalloc_stat_add(__n) __atomic_add_fetch(&used_memory, (__n), __ATOMIC_RELAXED) 4 #define update_zmalloc_stat_sub(__n) __atomic_sub_fetch(&used_memory, (__n), __ATOMIC_RELAXED) 5 #elif defined(HAVE_ATOMIC)//這個是GCC從某個版本開始支援的,不是所有版本的GCC都支援sync系列函數。 6 #define update_zmalloc_stat_add(__n) __sync_add_and_fetch(&used_memory, (__n)) 7 #define update_zmalloc_stat_sub(__n) __sync_sub_and_fetch(&used_memory, (__n)) 8 #else//不支援原子操作的情況下只能使用互斥鎖了。 9 #define update_zmalloc_stat_add(__n) do { 10 pthread_mutex_lock(&used_memory_mutex); 11 used_memory += (__n); 12 pthread_mutex_unlock(&used_memory_mutex); 13 } while(0)14 15 #define update_zmalloc_stat_sub(__n) do { 16 pthread_mutex_lock(&used_memory_mutex); 17 used_memory -= (__n); 18 pthread_mutex_unlock(&used_memory_mutex); 19 } while(0)20 21 #endif22 //更新記憶體使用量變數函數,函數內確定是否需要多安全執行緒23 #define update_zmalloc_stat_alloc(__n) do { 24 size_t _n = (__n); 25 if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); 26 if (zmalloc_thread_safe) { 27 update_zmalloc_stat_add(_n); 28 } else { 29 used_memory += _n; 30 } 31 } while(0)32 //更新記憶體使用量變數函數,函數內確定是否需要多安全執行緒33 #define update_zmalloc_stat_free(__n) do 34 { \ 35 size_t _n = (__n); \ 36 if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \ 37 if (zmalloc_thread_safe) 38 { \ 39 update_zmalloc_stat_sub(_n); \ 40 }41 else 42 { \ 43 used_memory -= _n; \ 44 } \ 45 } while(0)
下一篇再寫具體的函數實現。
Redis記憶體管理(一)