欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

redis 源代碼分析(一) 內(nèi)存管理

系統(tǒng) 4055 0

一,redis內(nèi)存管理介紹

? redis是一個(gè)基于內(nèi)存的key-value的數(shù)據(jù)庫(kù),其內(nèi)存管理是很重要的,為了屏蔽不同平臺(tái)之間的差異,以及統(tǒng)計(jì)內(nèi)存占用量等,redis對(duì)內(nèi)存分配函數(shù)進(jìn)行了一層封裝,程序中統(tǒng)一使用zmalloc,zfree一系列函數(shù),其相應(yīng)的源代碼在 src/zmalloc.h src/zmalloc.c 兩個(gè)文件里,源代碼點(diǎn) 這里

二,redis內(nèi)存管理源代碼分析

redis封裝是為了屏蔽底層平臺(tái)的差異,同一時(shí)候方便自己實(shí)現(xiàn)相關(guān)的函數(shù),我們能夠通過 src/zmalloc.h 文件里的相關(guān)宏定義來分析redis是怎么實(shí)現(xiàn)底層平臺(tái)差異的屏蔽的, zmalloc.h 中相關(guān)宏聲明例如以下

      #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

#ifndef ZMALLOC_LIB
#define ZMALLOC_LIB "libc"
#endif

...
#ifndef HAVE_MALLOC_SIZE
size_t zmalloc_size(void *ptr);
#endif


    

    

通過上面的宏的預(yù)處理我們能夠發(fā)現(xiàn)redis為了屏蔽不同系統(tǒng)(庫(kù))的差異進(jìn)行了例如以下預(yù)處理:

A ,若系統(tǒng)中存在Google的TC_MALLOC庫(kù),則使用tc_malloc一族函數(shù)取代原本的malloc一族函數(shù)。

B ,若系統(tǒng)中存在FaceBook的JEMALLOC庫(kù),則使用je_malloc一族函數(shù)取代原本的malloc一族函數(shù)。 ??

C ,若當(dāng)前系統(tǒng)是Mac系統(tǒng),則使用<malloc/malloc.h>中的內(nèi)存分配函數(shù)。???

D ,其它情況,在每一段分配好的空間前頭,同一時(shí)候多分配一個(gè)定長(zhǎng)的字段,用來記錄分配的空間大小。?

tc_malloc是google開源處理的一套內(nèi)存管理庫(kù),是用C++實(shí)現(xiàn)的,主頁(yè)在 這里 。TCMalloc給每一個(gè)線程分配了一個(gè)線程局部緩存。小分配能夠直接由線程局部緩存來滿足。須要的話,會(huì)將對(duì)象從中央數(shù)據(jù)結(jié)構(gòu)移動(dòng)到線程局部緩存中,同一時(shí)候定期的垃圾收集將用于把內(nèi)存從線程局部緩存遷移回中央數(shù)據(jù)結(jié)構(gòu)中。這篇 文章 里對(duì)TCMalloc有個(gè)具體的介紹。

jemalloc 也是一個(gè)內(nèi)存創(chuàng)管理庫(kù),其創(chuàng)始人Jason Evans也是在FreeBSD非常有名的開發(fā)者 ,參見 這里 。Jemalloc聚集了malloc的使用過程中所驗(yàn)證的非常多技術(shù)。忽略細(xì)節(jié),從架構(gòu)著眼,最出色的部分仍是arena和thread cache。

讀者一定會(huì)有疑問系統(tǒng)不是有了malloc 嗎,為什么還有這種內(nèi)存管理庫(kù)?? 因?yàn)榻?jīng)典的libc的分配器碎片率為較高,能夠查看 這篇文章 的分析,關(guān)于內(nèi)存碎片不太了解的童鞋請(qǐng)參考 這里 ,?malloc 和free 怎么工作的參考 這里 。 關(guān)于ptmalloc,tcmalloc和jemalloc內(nèi)存分配策略的一篇總結(jié)不錯(cuò)的文章,請(qǐng)點(diǎn) 這里

以下介紹redis封裝的內(nèi)存管理相關(guān)函數(shù), src/zmalloc.h 有相關(guān)聲明。

      void *zmalloc(size_t size);//malloc
void *zcalloc(size_t size);//calloc
void *zrealloc(void *ptr, size_t size);/realloc
void zfree(void *ptr);//free
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);
    

如今主要介紹下redis內(nèi)存分配函數(shù) void *zmalloc(size_t size),其相應(yīng)的聲明形式例如以下:

      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
}
    

閱讀源代碼我們發(fā)現(xiàn)有個(gè)PREFIX_SIZE 宏,其宏定義形式例如以下:

      /* zmalloc.c */
#ifdef HAVE_MALLOC_SIZE
#define PREFIX_SIZE (0)       
#else  
#if defined(__sun)
#define PREFIX_SIZE (sizeof(long long))
#else                         
#define PREFIX_SIZE (sizeof(size_t))
#endif
#endif
    

結(jié)合 src/zmalloc.h 有相關(guān)宏聲明,我們發(fā)現(xiàn),由于 tc_malloc 、je_malloc 和 Mac平臺(tái)下的 malloc 函數(shù)族提供了計(jì)算已分配空間大小的函數(shù)(各自是tc_malloc_size, je_malloc_usable_size和malloc_size),所以就不須要單獨(dú)分配一段空間記錄大小了。在linux和sun平臺(tái)則要記錄分配空間大小。對(duì)于linux,使用sizeof(size_t)定長(zhǎng)字段記錄;對(duì)于sun 系統(tǒng),使用sizeof(long long)定長(zhǎng)字段記錄,其相應(yīng)源代碼中的 PREFIX_SIZE 宏。

PREFIX_SIZE 有什么用呢?

為了統(tǒng)計(jì)當(dāng)前進(jìn)程究竟占用了多少內(nèi)存。在 zmalloc.c 中,有一個(gè)靜態(tài)變量:

      static size_t used_memory = 0;
    
這個(gè)變量它記錄了進(jìn)程當(dāng)前占用的內(nèi)存總數(shù)。每當(dāng)要分配內(nèi)存或是釋放內(nèi)存的時(shí)候,都要更新這個(gè)變量(當(dāng)然能夠是線程安全的)。由于分配內(nèi)存的時(shí)候,須要指定分配多少內(nèi)存。可是釋放內(nèi)存的時(shí)候,(對(duì)于未提供malloc_size函數(shù)的內(nèi)存庫(kù))通過指向要釋放內(nèi)存的指針是不能知道釋放的空間究竟有多大的。這時(shí)候,上面提到的PREFIX_SIZE就起作用了,能夠通過當(dāng)中記錄的內(nèi)容得到空間的大小。(只是在linux系統(tǒng)上也有對(duì)應(yīng)的函數(shù)獲得分配內(nèi)存空間的大小,參見 這里 )。

通過zmalloc的源代碼我們能夠發(fā)現(xiàn),其分配空間代碼為void *ptr = malloc(size+PREFIX_SIZE); 顯然其分配空間大小為:size+PREFIX_SIZE ,對(duì)于使用tc_malloc或je_malloc的情況或mac系統(tǒng),其 PREFIX_SIZE 為0。當(dāng)分配失敗時(shí)有對(duì)應(yīng)的出錯(cuò)處理 。

前面我們已經(jīng)說過redis通過使用used_memory 的變量來統(tǒng)計(jì)當(dāng)前進(jìn)程究竟占用了多少內(nèi)存,因此在分配和釋放內(nèi)存時(shí)我們須要緊接著更新used_memory 的相應(yīng)值,相應(yīng)到redis源代碼中為:

      #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
    
上面的代碼有事宏預(yù)處理 #ifdef HAVE_MALLOC_SIZE 顯然是上面我們說過的利用的tc_malloc je_malloc Mac等提供malloc_size函數(shù)的情形,我們能夠非常easy得知分配內(nèi)存的大小通過統(tǒng)一化的malloc_size函數(shù)就可以。可是對(duì)于沒有提供malloc_size功能的函數(shù),redis是怎么處理的呢?看上面的源代碼 #else以下的代碼即是事實(shí)上現(xiàn),其相應(yīng)的內(nèi)存結(jié)構(gòu)例如以下:
prefix-size memory size
分配的內(nèi)存前加一個(gè)固定大小的prefis-size空間,用于記錄該段內(nèi)存的大小,size所占領(lǐng)的內(nèi)存大小是已知的,為size_t類型的長(zhǎng)度,因此通過*((size_t*)ptr) = size; 就可以對(duì)當(dāng)前內(nèi)存塊大小進(jìn)行指定。每次分配內(nèi)存后,返回的實(shí)際地址指針為指向memorysize的地址( (char*)ptr+PREFIX_SIZE; ),通過該指針,能夠非常easy的計(jì)算出實(shí)際內(nèi)存的頭地址,從而釋放內(nèi)存。

redis通過update_zmalloc_stat_alloc(__n,__size) 和 update_zmalloc_stat_free(__n) 這兩個(gè)宏負(fù)責(zé)在分配內(nèi)存或是釋放內(nèi)存的時(shí)候更新used_memory變量。update_zmalloc_stat_alloc定義例如以下:

      #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)
    
redis把這個(gè)更新操作寫成宏的形式主要是處于效率的考慮。

上面的代碼中?

A,if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1));
? 主要是考慮對(duì)齊問題,保證新增的_n 是 sizeof(long)的倍數(shù)。

B, ? if (zmalloc_thread_safe) { \
??????? update_zmalloc_stat_add(_n); \

?????? }

假設(shè)進(jìn)程中有多個(gè)線程存在,并保證線程安全zmalloc_thread_safe,則在更新變量的時(shí)候要加鎖。? 通過宏HAVE_ATOMIC選擇對(duì)應(yīng)的同步機(jī)制。

zmalloc_calloc、zmalloc_free等的實(shí)現(xiàn)就不細(xì)致介紹了詳情參見 源代碼

最后解說下 zmalloc_get_rss() 函數(shù)。
?? 這個(gè)函數(shù)用來獲取進(jìn)程的RSS。神馬是RSS?全稱為Resident Set Size,指實(shí)際使用物理內(nèi)存(包括共享庫(kù)占用的內(nèi)存)。在linux系統(tǒng)中,能夠通過讀取/proc/pid/stat文件系統(tǒng)獲取,pid為當(dāng)前進(jìn)程的進(jìn)程號(hào)。讀取到的不是byte數(shù),而是內(nèi)存頁(yè)數(shù)。通過系統(tǒng)調(diào)用sysconf(_SC_PAGESIZE)能夠獲得當(dāng)前系統(tǒng)的內(nèi)存頁(yè)大小。 獲得進(jìn)程的RSS后,能夠計(jì)算眼下數(shù)據(jù)的內(nèi)存碎片大小,直接用rss除以u(píng)sed_memory。rss包括進(jìn)程的全部?jī)?nèi)存使用,包括代碼,共享庫(kù),堆棧等。 哪來的內(nèi)存碎片?上面我們已經(jīng)說明了通常考慮到效率,往往有內(nèi)存對(duì)齊等方面的考慮,所以,碎片就在這里產(chǎn)生了。相比傳統(tǒng)glibc中的malloc的內(nèi)存利用率不是非常高通常會(huì)使用別的內(nèi)存庫(kù)系統(tǒng)。在redis中默認(rèn)的已經(jīng)不使用簡(jiǎn)單的malloc了而是使用 jemalloc, 在源文件src/Makefile下有這樣一段代碼:

      
ifeq ($(uname_S),Linux)
MALLOC = jemalloc
能夠知道在linux系統(tǒng)上默認(rèn)使用jemalloc, 在redis公布的源代碼中有相關(guān)的庫(kù) deps/jemalloc


總的來說 redis則全然自主分配內(nèi)存,在請(qǐng)求到的時(shí)候?qū)崟r(shí)依據(jù)內(nèi)建的算法分配內(nèi)存,全然自主控制內(nèi)存的管理。簡(jiǎn)單即是美吧,只是功能確實(shí)強(qiáng)大。


參考:

http://blog.ddup.us/?p=136

redis 源代碼分析(一) 內(nèi)存管理


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 三级理伦| 欧美网站在线看 | 丁香婷婷久久 | 中文字幕亚洲精品 | 久久精品小视频 | 国产99久久精品一区二区永久免费 | 久久这里只有精品免费播放 | 在线观看91精品国产入口 | 亚洲精选一区 | 日韩精品一区二区三区中文字幕 | 午夜视频在线观看网站 | 欧美激情一区二区三级高清视频 | 免费一级在线 | 午夜在线电影 | 免费黄色大片 | 午夜成人免费视频 | 久久亚洲日本不卡一区二区 | 日韩在线短视频 | 久久久日韩精品一区二区 | sese综合 | 亚洲热线99精品视频 | 欧美14一15sex性hd | 亚洲午夜av | 99亚洲视频| 国产精品亚洲天堂 | 国产欧美日韩精品一区 | 日韩免费视频网站 | 亚洲一区二区三区四区在线观看 | 久久亚洲国产成人亚 | 欧美精品一区二区三区久久 | 亚洲午夜在线 | 久久精精 | 久久精品蜜芽亚洲国产a | 久久艹逼 | 天天干夜夜夜 | 欧美一级视频 | 亚洲精品福利一区二区三区 | 日本免费成人 | 99免费观看视频 | 一区二区中文字幕 | 亚洲国产成人在线视频 |