redis源碼解析之記憶體管理

來源:互聯網
上載者:User

標籤:style   class   blog   code   http   tar   

 zmalloc.h的內容如下:
1 void *zmalloc(size_t size);2  void *zcalloc(size_t size);3  void *zrealloc(void *ptr, size_t size);4  void zfree(void *ptr);5  char *zstrdup(const char *s);6 size_t zmalloc_used_memory(void);7  void zmalloc_enable_thread_safeness(void);8  float zmalloc_get_fragmentation_ratio(void);9 size_t zmalloc_get_rss(void);10 size_t zmalloc_allocations_for_size(size_t size);11 12  #define ZMALLOC_MAX_ALLOC_STAT 256
就這麼幾行 這是redis的記憶體管理介面。zmalloc,zcalloc,zrealloc和zfree分別對應c庫中的malloc,calloc,realloc和free zstrdup用於產生一個字串的拷貝。後面的幾個函數用於擷取記憶體使用量資訊,後面會詳細介紹。
在看zmalloc.c的源碼之前,先看看redis是怎樣管理記憶體的 redis為了方便記憶體的管理,在分配一塊記憶體之後,會將這塊記憶體的大小存入記憶體塊的頭部。
real_ptr是redis調用malloc後返回的指標 redis將記憶體塊的大小size存入頭部,size所佔據的記憶體大小是已知的,為size_t類型的長度,然後返回ret_ptr 當需要釋放記憶體的時候,ret_ptr被傳給記憶體管理程式。通過ret_ptr,程式可以很容易的算出real_ptr的值,然後將real_ptr傳給free釋放記憶體
redis會記錄所有的記憶體配置情況。redis定義一個數組,這個數組的長度為ZMALLOC_MAX_ALLOC_STAT。數組的每一個元素代表當前程式所分配的記憶體塊的個數,且記憶體塊的大小為該元素的下標 在程式中,這個數組為zmalloc_allocations。zmalloc_allocations[16]代表已經分配的長度為16bytes的記憶體塊的個數 zmalloc.c中有一個靜態變數used_memory用來記錄當前分配的記憶體總大小
下面開始分析代碼。
首先是定義宏PREFIX_SIZE:
1 #ifdef HAVE_MALLOC_SIZE2  #define PREFIX_SIZE (0)3  #else4  #if defined(__sun)5  #define PREFIX_SIZE (sizeof(long long))6  #else7  #define PREFIX_SIZE (sizeof(size_t))8  #endif9  #endif
如果定義了HAVE_MALLOC_SIZE,那麼PREFIX_SIZE就為0 這裡,HAVE_MALLOC_SIZE用來確定系統是否有函數malloc_size。這個HAVE_MALLOC_SIZE宏在config .h中定義,如下:
1 /* Use tcmalloc‘s malloc_size() when available.2 * When tcmalloc is used, native OSX malloc_size() may never be used because3 * this expects a different allocation scheme. Therefore, *exclusively* use4 * either tcmalloc or OSX‘s malloc_size()! */5  #if defined(USE_TCMALLOC)6 #include <google/tcmalloc.h>7 #if TC_VERSION_MAJOR >= 1 && TC_VERSION_MINOR >= 68 #define HAVE_MALLOC_SIZE 19 #define redis_malloc_size(p) tc_malloc_size(p)10 #endif11 #elif defined(__APPLE__)12 #include <malloc/malloc.h>13 #define HAVE_MALLOC_SIZE 114 #define redis_malloc_size(p) malloc_size(p)15 #endif
如果使用google的tcmalloc庫,那麼,redis_malloc_size就對應與tcmalloc庫的tc_malloc_size函數 如果是在apple的mac上編譯,那麼redis_malloc_size就對應與malloc_size redis_malloc_size的功能是獲得參數p所指向的記憶體塊的大小 tcmalloc庫在google的google-perftools 庫中,據說這個庫在記憶體管理的效率上很驚豔 不過這個庫是c++寫的,而redis是c寫的,兩者揉一起還是有點不給力阿

如果沒有malloc_size函數,那麼在Solaris系統上,用long long類型的長度來定義PREFIX_SIZE,其他系統為size_t的長度

接著,定義下面這些宏。這些宏的作用是如果使用tcmalloc庫,那麼將庫中的分配函數對應到標準庫上 後面的函數可直接使用標準庫函數的名稱。在更換庫的時候不需要更改。
1 /* Explicitly override malloc/free etc when using tcmalloc. */2 #if defined(USE_TCMALLOC)3 #define malloc(size) tc_malloc(size)4 #define calloc(count,size) tc_calloc(count,size)5 #define realloc(ptr,size) tc_realloc(ptr,size)6 #define free(ptr) tc_free(ptr)7 #endif下面的兩個宏用於更新zmalloc_allocations數組。update_zmalloc_stat_alloc用於在分配記憶體的時候更新已指派大小,update_zmalloc_stat_free用於在釋放記憶體的時候刪除對應的記錄。1 #define update_zmalloc_stat_alloc(__n,__size) do { 2 size_t _n = (__n); 3 size_t _stat_slot = (__size < ZMALLOC_MAX_ALLOC_STAT) ? __size : ZMALLOC_MAX_ALLOC_STAT; 4 if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); 5 if (zmalloc_thread_safe) { 6 pthread_mutex_lock(&used_memory_mutex); 7 used_memory += _n; 8 zmalloc_allocations[_stat_slot]++; 9 pthread_mutex_unlock(&used_memory_mutex); 10 } else { 11 used_memory += _n; 12 zmalloc_allocations[_stat_slot]++; 13 } 14 } while(0)
update_zmalloc_stat_alloc的第一個參數__n是從系統那實際獲得的記憶體大小,第二個參數是程式請求的記憶體大小 update_zmalloc_stat_alloc首先判斷程式請求的記憶體大小在zmalloc_allocations數組中對應的下標 如果記憶體大小大於zmalloc_allocations數組的長度-1,那麼其對應的下標是最後一個。然後,將實際分配的記憶體大小對齊為long類型長度的整數倍(malloc通常會考慮對齊問題,實際分配的記憶體大小也會因對齊而有所出入,後文會介紹) 最後,在used_memory記錄實際分配的大小,在zmalloc_allocations對應位置加一 這裡如果設定為安全執行緒,那麼在記錄之前要對兩個靜態變數加鎖
1 #define update_zmalloc_stat_free(__n) do { 2 size_t _n = (__n); 3 if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); 4 if (zmalloc_thread_safe) { 5 pthread_mutex_lock(&used_memory_mutex); 6 used_memory -= _n; 7 pthread_mutex_unlock(&used_memory_mutex); 8 } else { 9 used_memory -= _n; 10 } 11 } while(0)
update_zmalloc_stat_free和update_zmalloc_stat_alloc差不多,但僅僅減少了used_memory的值
對於zmalloc,zalloc,zrealloc和zfree這幾個函數,僅僅是對標準庫的函數的簡單的封裝 所做的工作除了調用標準庫(也可能是tcmalloc庫)的函數分配記憶體外,就是對每次分配和釋放記憶體做合適的記錄 如果系統中有malloc_size函數,那麼直接調用前面的那兩個宏,沒什麼可講的 如果沒有malloc_size函數,那麼需要在所分配的記憶體頭部的PREFIX_SIZE大小的地區內,記錄記憶體塊的大小 代碼很簡單,就一句:1 *((size_t*)ptr) = size;現將ptr轉換成size_t類型的指標,然後將size的值賦給其指向的記憶體 筆者感覺沒有必要在前面定義PREFIX_SIZE的時候區分系統,因為這裡直接寫入程式碼了記憶體大小的類型為size_t 前面的宏判斷有點多此一舉了 這裡的size是返回的記憶體地區的大小,不包括儲存大小的頭部。
讀取記憶體塊大小需要兩步:1 realptr = (char*)ptr-PREFIX_SIZE;
2 oldsize = *((size_t*)realptr);程式傳進來的是ret_ptr,通過減去PREFIX_SIZE得到real_ptr 從real_ptr所指向的記憶體中讀取大小即可。
其餘的幾個函數也很直接,沒什麼好說的 最後講一講zmalloc_get_rss()函數。這個函數用來擷取進程的RSS。神馬是RSS?google reader那個?顯然不是。 全稱為Resident Set Size,指實際使用實體記憶體(包含共用庫佔用的記憶體) 在linux系統中,可以通過讀取/proc/pid/stat檔案擷取,pid為當前進程的進程號 讀取到的不是byte數,而是記憶體頁數。通過系統調用sysconf(_SC_PAGESIZE)可以獲得當前系統的記憶體頁大小 Unix系統貌似可以直接通過task_info直接擷取,比linux系統簡單的多。
獲得進程的RSS後,可以計算目前資料的記憶體片段大小,直接用rss除以used_memory rss包含進程的所有記憶體使用量,包括代碼,共用庫,堆棧等。但是由於通常情況下redis在記憶體中資料的量要遠遠大於這些資料所佔用的記憶體,因此這個簡單的計算還是比較準確的
這裡有一個問題,程式都是用多少記憶體就分配多少記憶體,哪來的記憶體片段?其實,當調用malloc的時候,malloc並不是嚴格按照參數的值來分配記憶體 比如,程式只請求一個byte的記憶體,malloc不會就只分配一個byte,通常,基於記憶體對齊等方面的考慮,malloc會分配4個byte 這樣,如果程式中大量請求1byte記憶體,那麼實際使用的是所請求的4倍。malloc進行小記憶體配置是很浪費的 所以,片段就在這裡產生了。
總的來說,redis的記憶體管理簡單粗暴,沒有神馬複雜的引用計數等技術 但是,很多時候,簡單的往往是高效且合理的。redis記憶體的中資料通常會是幾個G,這個方法快速,統計結果也很精確   coding trick:
#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)
先對_n的低位向上取整,最後_n變為sizeof(long)的倍數,比如對於32位系統,sizeof(long) == 100(二進位),_n向上取整之後,低兩位都變為0。分配的空間與記憶體對齊

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.