深入redis內部--內存管理


1. Redis內存管理通過在zmalloc.h和zmalloc.c中重寫c語言對內存的管理來完成的。

redis內存管理 c內存管理 原型 作用
zmalloc malloc void *malloc(unsigned int num_bytes); 分配一塊指定大小的內存區域,並返回指向該區域頭部的指針,分配失敗則返回NULL
zcalloc calloc void *calloc(unsigned n, unsigned size); 在內存的動態存儲區中分配n個長度為size的連續空間,函數返回一個指向分配起始地址的指針;如果分配不成功,返回NULL。
zrealloc realloc oid *realloc(void *mem_address, unsigned int newsize); 先判斷當前的指針是否有足夠的連續空間,如果有,擴大mem_address指向的地址,並且將mem_address返回,如果空間不夠,先按照newsize指定的大小分配空間,將原有數據從頭到尾拷貝到新分配的內存區域,而后釋放原來mem_address所指內存區域(注意:原來指針是自動釋放,不需要使用free),同時返回新分配的內存區域的首地址。即重新分配存儲器塊的地址。
zfree free void free(void *ptr)  釋放ptr指向的存儲空間。被釋放的空間通常被送入可用存儲區池,以后可在調用malloc、realloc以及calloc函數來再分配。

 

封裝就是為了屏蔽底層平台的差異,同時方便自己實現相關的統計函數。

定義平台之間的差異,主要是tcmalloc(google)、jemalloc(facebook)、蘋果平台。

具體來說就是:

  • 若系統中存在Google的TC_MALLOC庫,則使用tc_malloc一族函數代替原本的malloc一族函數。
  • 若系統中存在facebook的JE_MALLOC庫,則使用je_malloc一族函數替換原來的malloc一族函數。
  • 若當前系統是Mac系統或者其它系統,則使用<malloc/malloc.h>中的內存分配函數。
/* Double expansion needed for stringification of macro values. */
#define __xstr(s) __str(s)
#define __str(s) #s

#if defined(USE_TCMALLOC)
#define ZMALLOC_LIB ("tcmalloc-" __xstr(TC_VERSION_MAJOR) "." __xstr(TC_VERSION_MINOR))
#include <google/tcmalloc.h>
#if (TC_VERSION_MAJOR == 1 && TC_VERSION_MINOR >= 6) || (TC_VERSION_MAJOR > 1)
#define HAVE_MALLOC_SIZE 1
#define zmalloc_size(p) tc_malloc_size(p)
#else
#error "Newer version of tcmalloc required"
#endif

#elif defined(USE_JEMALLOC)
#define ZMALLOC_LIB ("jemalloc-" __xstr(JEMALLOC_VERSION_MAJOR) "." __xstr(JEMALLOC_VERSION_MINOR) "." __xstr(JEMALLOC_VERSION_BUGFIX))
#include <jemalloc/jemalloc.h>
#if (JEMALLOC_VERSION_MAJOR == 2 && JEMALLOC_VERSION_MINOR >= 1) || (JEMALLOC_VERSION_MAJOR > 2)
#define HAVE_MALLOC_SIZE 1
#define zmalloc_size(p) je_malloc_usable_size(p)
#else
#error "Newer version of jemalloc required"
#endif

#elif defined(__APPLE__)
#include <malloc/malloc.h>
#define HAVE_MALLOC_SIZE 1
#define zmalloc_size(p) malloc_size(p)
#endif

 

具體如下:

/* Explicitly override malloc/free etc when using tcmalloc. */
#if defined(USE_TCMALLOC)
#define malloc(size) tc_malloc(size)
#define calloc(count,size) tc_calloc(count,size)
#define realloc(ptr,size) tc_realloc(ptr,size)
#define free(ptr) tc_free(ptr)
#elif defined(USE_JEMALLOC)
#define malloc(size) je_malloc(size)
#define calloc(count,size) je_calloc(count,size)
#define realloc(ptr,size) je_realloc(ptr,size)
#define free(ptr) je_free(ptr)
#endif

#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

說明:

Both libraries try to de-contention memory acquire by having threads pick the memory from different caches, but they have different strategies:

 
        
  • jemalloc (used by Facebook) maintains a cache per thread
  • tcmalloc (from Google) maintains a pool of caches, and threads develop a "natural" affinity for a cache, but may change
 
        

This led, once again if I remember correctly, to an important difference in term of thread management.

 
        
  • jemalloc is faster if threads are static, for example using pools
  • tcmalloc is faster when threads are created/destructed

1.1 zmalloc實現      

void *zmalloc(size_t size) {
    void *ptr = malloc(size+PREFIX_SIZE);

    if (!ptr) zmalloc_oom_handler(size); //如果沒有發生內存溢出,則使用的分配方式static void (*zmalloc_oom_handler)(size_t) = zmalloc_default_oom;
#ifdef HAVE_MALLOC_SIZE                  //HAVE_MALLOC_SIZE用來確定系統是否有函數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
}

 

#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)

#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

說明

int pthread_mutex_lock(pthread_mutex_t *mutex);

當pthread_mutex_lock()返回時,該互斥鎖已被鎖定。線程調用該函數讓互斥鎖上鎖,如果該互斥鎖已被另一個線程鎖定和擁有,則調用該線程將阻塞,直到該互斥鎖變為可用為止。

int pthread_mutex_unlock(pthread_mutex_t *mutex);和上面的函數為一對。

其它函數的實現類似。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM