一、有了tcmalloc或者ptmalloc這樣的庫(kù),還要自己寫內(nèi)存池的原因
1、可以定制化
自己編寫內(nèi)存池可以滿足更加個(gè)性化的需求,有效降低內(nèi)存使用,避免內(nèi)存碎片問題。通常一個(gè)應(yīng)用程序擁有自己獨(dú)特的內(nèi)存使用模式,自己編寫內(nèi)存池可以為特定的應(yīng)用程序場(chǎng)景量身定制。
2、內(nèi)存分配效率更高
自己編寫內(nèi)存池可以直接申請(qǐng)一大塊內(nèi)存空間,有效減少申請(qǐng)內(nèi)存的次數(shù)和時(shí)間。內(nèi)存池中的內(nèi)存可以被重復(fù)利用,降低內(nèi)存申請(qǐng)和釋放造成的成本。
3、可以減少鎖的爭(zhēng)用
在多線程環(huán)境下,TCMalloc和ptmalloc庫(kù)的內(nèi)部實(shí)現(xiàn)會(huì)使用同步機(jī)制來(lái)保證并發(fā)訪問的正確性,會(huì)造成鎖的爭(zhēng)用現(xiàn)象。自己編寫內(nèi)存池可以減少同步機(jī)制的使用,提高訪問速度。
4、可以更好的控制
自己編寫的內(nèi)存池可以比庫(kù)更好地控制內(nèi)存分配和釋放的行為,例如在某一個(gè)時(shí)間停止分配內(nèi)存等操作。
二、tcmalloc介紹
1、簡(jiǎn)介
TCMalloc 是 Google 對(duì) C 的 malloc() 和 C++ 的 operator new 的自定義實(shí)現(xiàn),用于在我們的 C 和 C++ 代碼中進(jìn)行內(nèi)存分配。 TCMalloc 是一種快速、多線程的 malloc 實(shí)現(xiàn)。
? TCMalloc為每個(gè)線程分配了緩存,這個(gè)緩存是線程私有的,可以減少多線程程序競(jìng)爭(zhēng)。對(duì)于小對(duì)象的內(nèi)存分配,首先會(huì)去請(qǐng)求線程緩存,不用加鎖,如果緩存不能滿足的話,需要去向后面的內(nèi)存存儲(chǔ)結(jié)構(gòu)中獲取,此時(shí)需要加鎖獲取,因?yàn)槠渌€程可能正在獲取內(nèi)存空間,但是大部分情況下線程緩存就可以滿足內(nèi)存請(qǐng)求,所以幾乎不需要鎖。對(duì)于大對(duì)象的內(nèi)存分配,TCMalloc嘗試著使用細(xì)粒度和高效的自旋鎖。另外一個(gè)TCMalloc的好處是小對(duì)象內(nèi)存分配效率高。例如,分配n個(gè)8 byte的對(duì)象時(shí),使用大約8n * 1.01byte的空間,只有百分之一的空間浪費(fèi)。ptmalloc2分配內(nèi)存的方法為每個(gè)對(duì)象使用一個(gè)4 byte的標(biāo)頭,并且將大小四舍五入為8 byte的倍數(shù),最終使用16n byte。
2、TCMalloc架構(gòu)
我們可以將TCMlloc分為三部分:front-end;middle-end;back-end。 它們的職責(zé)分別是:
Front-end:是一個(gè)緩存,提供內(nèi)存快速分配和重分配內(nèi)存給應(yīng)用程序的功能。它主要有2部分組成:Per-thread cache和Per-CPU cache。Middle-end:負(fù)責(zé)給front-end提供緩存。當(dāng)front-end緩存不足時(shí),首先從middle-end中獲取。它由Central free list組成。Back-end:負(fù)責(zé)從系統(tǒng)獲取內(nèi)存。當(dāng)middle-end中的內(nèi)存不足時(shí),從back-end中獲取。它主要設(shè)計(jì)page heap的內(nèi)容。3、小對(duì)象和大對(duì)象分配
TCMalloc維護(hù)了一份空間大小映射表,當(dāng)分配小對(duì)象內(nèi)存空間時(shí),會(huì)從這個(gè)表里尋找合適大小的內(nèi)存,點(diǎn)這里能都看到。例如,12字節(jié)的分配將會(huì)尋找16字節(jié)大小的內(nèi)存空間。空間大小級(jí)別是為了在向上取最小滿足的內(nèi)存空間時(shí)減少浪費(fèi)。
? 如果分配的內(nèi)存空間大于1MB,那么直接從后端分配,因此,大對(duì)象內(nèi)存空間不會(huì)緩存在front-end和middle-end中。大對(duì)象分配時(shí)會(huì)向上取最小滿足頁(yè)大小的內(nèi)存空間。
Middle-end負(fù)責(zé)為 Front-end提供內(nèi)存以及把多余的內(nèi)存放回Back-end。Middle-end是由Transfer cache和Central free list組成。盡管Transfer cache和Central free list經(jīng)常被認(rèn)為是一個(gè)東西,但它們是有區(qū)別的。當(dāng)Front-end訪問Middle-end時(shí)需要先加鎖然后再獲取內(nèi)存,這會(huì)造成線性訪問的時(shí)間消耗。
三、內(nèi)存池介紹
1、基本概念
(Memory Pool)是一種內(nèi)存分配方式,又被稱為固定大小區(qū)塊規(guī)劃(fixed-size-blocks allocation)。通常我們習(xí)慣直接使用new、malloc等API申請(qǐng)分配內(nèi)存,這樣做的缺點(diǎn)在于:由于所申請(qǐng)內(nèi)存塊的大小不定,當(dāng)頻繁使用時(shí)會(huì)造成大量的內(nèi)存碎片并進(jìn)而降低性能。
內(nèi)存池則是在真正使用內(nèi)存之前,先申請(qǐng)分配一定數(shù)量的、大小相等(一般情況下)的內(nèi)存塊留作備用。當(dāng)有新的內(nèi)存需求時(shí),就從內(nèi)存池中分出一部分內(nèi)存塊,若內(nèi)存塊不夠再繼續(xù)申請(qǐng)新的內(nèi)存。這樣做的一個(gè)顯著優(yōu)點(diǎn)是,使得內(nèi)存分配效率得到提升。在內(nèi)核中有不少地方內(nèi)存分配不允許失敗。作為一個(gè)在這些情況下確保分配的方式,內(nèi)核開發(fā)者創(chuàng)建了一個(gè)已知為內(nèi)存池(或者是“mempool”)的抽象。一個(gè)內(nèi)存池真實(shí)地只是一類后備緩存,它盡力一直保持一個(gè)空閑內(nèi)存列表給緊急時(shí)使用。
2、實(shí)現(xiàn)示例
內(nèi)存池的實(shí)現(xiàn)有很多,性能和適用性也不相同,以下是一種較簡(jiǎn)單的C++實(shí)現(xiàn)—GenericMP模板類。在這個(gè)例子中,使用了模板以適應(yīng)不同對(duì)象的內(nèi)存需求,內(nèi)存池中的內(nèi)存塊則是以基于鏈表的結(jié)構(gòu)進(jìn)行組織。GenericMP模板類定義:
template class GenericMP{public:static VOID *operator new(size_t allocLen){assert(sizeof(T) == allocLen);if(!m_NewPointer)MyAlloc();UCHAR *rp = m_NewPointer;m_NewPointer = *reinterpret_cast(rp); //由于頭4個(gè)字節(jié)被“強(qiáng)行”解釋為指向下一內(nèi)存塊的指針,這里m_NewPointer就指向了下一個(gè)內(nèi)存塊,以備下次分配使用。return rp;}static VOID operator delete(VOID *dp){*reinterpret_cast(dp) = m_NewPointer;m_NewPointer = static_cast(dp);}private:static VOID MyAlloc(){m_NewPointer = new UCHAR[sizeof(T) * BLOCK_NUM];UCHAR **cur = reinterpret_cast(m_NewPointer); //強(qiáng)制轉(zhuǎn)型為雙指針,這將改變每個(gè)內(nèi)存塊頭4個(gè)字節(jié)的含義。UCHAR *next = m_NewPointer;for(INT i = 0; i < BLOCK_NUM-1; i++){next += sizeof(T);*cur = next;cur = reinterpret_cast(next); //這樣,所分配的每個(gè)內(nèi)存塊的頭4個(gè)字節(jié)就被“強(qiáng)行“解釋為指向下一個(gè)內(nèi)存塊的指針, 即形成了內(nèi)存塊的鏈表結(jié)構(gòu)。}*cur = 0;}static UCHAR *m_NewPointer;protected:~GenericMP(){}};templateUCHAR *GenericMP::m_NewPointer;GenericMP模板類應(yīng)用class ExpMP : public GenericMP{BYTE a[1024];};int _tmain(int argc, _TCHAR* argv[]){ExpMP *aMP = new ExpMP();delete aMP;}
延伸閱讀1:ptmalloc簡(jiǎn)介
ptmalloc是glibc默認(rèn)的內(nèi)存管理器。我們常用的malloc和free就是由ptmalloc內(nèi)存管理器提供的基礎(chǔ)內(nèi)存分配函數(shù)。ptmalloc有點(diǎn)像我們自己寫的內(nèi)存池,當(dāng)我們通過(guò)malloc或者free函數(shù)來(lái)申請(qǐng)和釋放內(nèi)存的時(shí)候,ptmalloc會(huì)將這些內(nèi)存管理起來(lái),并且通過(guò)一些策略來(lái)判斷是否需要回收給操作系統(tǒng)。