這個(gè)內(nèi)存池之前已經(jīng)介紹過(guò)了,在
這里這次整理自己的服務(wù)器公共庫(kù),沒(méi)有忘記這個(gè)好東西,算法沒(méi)有改變,但是有兩個(gè)變化:
1) 我認(rèn)為這個(gè)模塊對(duì)于一個(gè)服務(wù)器而言, 應(yīng)該是一個(gè)單件, 所以它繼承自前面說(shuō)過(guò)的singleton基類.
2) 加入了線程鎖, 同樣是可以配置的, 因?yàn)槲一静粚?xiě)多線程的服務(wù)器, 不過(guò), 還是把接口保留在那里吧, 如果需要可以打開(kāi)以支持多線程.
mempool.h:
/********************************************************************
created: 2008/08/03
filename: mempool.h
author: Lichuang
purpose: 仿SGI STL內(nèi)存池的實(shí)現(xiàn)
*********************************************************************/
#ifndef __MEM_POOL_H__
#define __MEM_POOL_H__
#include "singleton.h"
#include "threadmutex.h"
#include <stdio.h>
// 字節(jié)對(duì)齊數(shù)
#define ALIGN 512
// 最大BLOCK的尺寸
#define MAX_BLOCK_SIZE 20 * 1024
// HASH表的數(shù)量
#define BLOCK_LIST_NUM (MAX_BLOCK_SIZE) / (ALIGN)
// HASH表中每次初始化時(shí)LIST中元素的數(shù)量
#define LIST_NODE_NUM 20
class CMemPool
: CSingleton<CMemPool>
{
public:
void* Allocate(size_t nSize);
void* Reallocate(void* p, size_t nOldSize, size_t nNewSize);
void Deallocate(void* p, size_t nSize);
int GetMemSize();
private:
CMemPool();
virtual ~CMemPool();
char* AllocChunk(size_t nSize, int& nObjs);
void* Refill(size_t n);
size_t RoundUp(size_t nBytes);
int GetFreeListIndex(size_t nBytes);
private:
DECLARE_SINGLETON_CLASS(CMemPool)
private:
union Obj
{
union Obj* pFreeListLink;
char szData[1];
};
Obj* m_szFreeList[BLOCK_LIST_NUM];
char* m_pStartFree;
char* m_pEndFree;
size_t m_nHeapSize;
CThreadMutex m_tThreadMutex;
};
#endif /* __MEM_POOL_H__ */
mempool.cpp:
/********************************************************************
created: 2008/08/01
filename: mempool.h
author: Lichuang
purpose: 模擬SGI STL內(nèi)存池的實(shí)現(xiàn), 可以配置是否支持多線程
*********************************************************************/
#include "mempool.h"
#include <string.h>
CMemPool::CMemPool()
: m_pStartFree(NULL)
, m_pEndFree(NULL)
, m_nHeapSize(0)
{
::memset(m_szFreeList, 0, sizeof(m_szFreeList));
}
CMemPool::~CMemPool()
{
}
// 從內(nèi)存池中分配尺寸為n的內(nèi)存
void* CMemPool::Allocate(size_t nSize)
{
if (nSize > MAX_BLOCK_SIZE)
{
return ::malloc(nSize);
}
if (0 >= nSize)
{
return NULL;
}
Obj** ppFreeList;
Obj* pResult;
THREAD_LOCK;
// 獲得尺寸n的HASH表地址
ppFreeList = m_szFreeList + GetFreeListIndex(nSize);
pResult = *ppFreeList;
if (NULL == pResult)
{
// 如果之前沒(méi)有分配, 或者已經(jīng)分配完畢了, 就調(diào)用refill函數(shù)重新分配
// 需要注意的是, 傳入refill的參數(shù)是經(jīng)過(guò)對(duì)齊處理的
pResult = (Obj*)Refill(RoundUp(nSize));
}
else
{
// 否則就更新該HASH表的LIST頭節(jié)點(diǎn)指向下一個(gè)LIST的節(jié)點(diǎn), 當(dāng)分配完畢時(shí), 頭結(jié)點(diǎn)為NULL
*ppFreeList = pResult->pFreeListLink;
}
THREAD_UNLOCK;
return pResult;
}
void* CMemPool::Reallocate(void* p, size_t nOldSize, size_t nNewSize)
{
void* pResult;
size_t nCopySize;
// 如果超過(guò)內(nèi)存池所能承受的最大尺寸, 調(diào)用系統(tǒng)API重新分配內(nèi)存
if (nOldSize > (size_t)MAX_BLOCK_SIZE && nNewSize > (size_t)MAX_BLOCK_SIZE)
{
return ::realloc(p, nNewSize);
}
// 如果新老內(nèi)存尺寸在對(duì)齊之后相同, 那么直接返回
if (RoundUp(nOldSize) == RoundUp(nNewSize))
return p;
// 首先按照新的尺寸分配內(nèi)存
pResult = Allocate(nNewSize);
if (NULL == pResult)
{
return NULL;
}
// copy舊內(nèi)存的數(shù)據(jù)到新的內(nèi)存區(qū)域
nCopySize = nNewSize > nOldSize ? nOldSize : nNewSize;
::memcpy(pResult, p, nCopySize);
// 釋放舊內(nèi)存區(qū)域
Deallocate(p, nOldSize);
return pResult;
}
// 將尺寸為n的內(nèi)存回收到內(nèi)存池中
void CMemPool::Deallocate(void* p, size_t nSize)
{
Obj* pObj = (Obj *)p;
Obj **ppFreeList;
if (0 >= nSize)
{
return;
}
// 如果要回收的內(nèi)存大于MAX_BLOCK_SIZE, 直接調(diào)用free回收內(nèi)存
if (nSize > MAX_BLOCK_SIZE)
{
::free(p);
return;
}
// 將回收的內(nèi)存作為鏈表的頭回收
ppFreeList = m_szFreeList + GetFreeListIndex(nSize);
THREAD_LOCK;
pObj->pFreeListLink = *ppFreeList;
*ppFreeList = pObj;
THREAD_UNLOCK;
}
int CMemPool::GetMemSize()
{
return m_nHeapSize;
}
size_t CMemPool::RoundUp(size_t nBytes)
{
return (nBytes + ALIGN - 1) & ~(ALIGN - 1);
}
int CMemPool::GetFreeListIndex(size_t nBytes)
{
return (nBytes + ALIGN - 1) / ALIGN - 1;
}
char* CMemPool::AllocChunk(size_t nSize, int& nObjs)
{
char* pResult;
// 總共所需的內(nèi)存
size_t nTotalBytes = nSize * nObjs;
// 剩余的內(nèi)存
size_t nBytesLeft = m_pEndFree - m_pStartFree;
// 如果剩余的內(nèi)存可以滿足需求, 就直接返回之, 并且更新內(nèi)存池的指針
if (nBytesLeft >= nTotalBytes)
{
pResult = m_pStartFree;
m_pStartFree += nTotalBytes;
return pResult;
}
// 如果剩余的內(nèi)存大于單位內(nèi)存數(shù)量, 也就是說(shuō)至少還可以分配一個(gè)單位內(nèi)存
// 計(jì)算出最多可以分配多少塊單位內(nèi)存, 保存至nobjs, 返回內(nèi)存的指針
if (nBytesLeft >= nSize)
{
nObjs = (int)(nBytesLeft / nSize);
nTotalBytes = nSize * nObjs;
pResult = m_pStartFree;
m_pStartFree += nTotalBytes;
return pResult;
}
// 如果還有剩余的內(nèi)存, 將它放到對(duì)應(yīng)的HASH-LIST頭部
if (0 < nBytesLeft)
{
Obj** ppFreeList = m_szFreeList + GetFreeListIndex(nBytesLeft);
((Obj*)m_pStartFree)->pFreeListLink = *ppFreeList;
*ppFreeList = (Obj*)m_pStartFree;
}
// 需要獲取的內(nèi)存, 注意第一次分配都要兩倍于total_bytes的大小
// 同時(shí)要加上原有的heap_size / 4的對(duì)齊值
size_t nBytesToGet = 2 * nTotalBytes + RoundUp(m_nHeapSize >> 4);
m_pStartFree = (char*)::malloc(nBytesToGet);
// 獲取成功 重新調(diào)用chunk_alloc函數(shù)分配內(nèi)存
if (NULL != m_pStartFree)
{
m_nHeapSize += nBytesToGet;
m_pEndFree = m_pStartFree + nBytesToGet;
return AllocChunk(nSize, nObjs);
}
// 下面是獲取不成功的處理
.
// 從下一個(gè)HASH-LIST中尋找可用的內(nèi)存
int i = (int)GetFreeListIndex(nSize) + 1;
Obj **ppFreeList, *p;
for (; i < BLOCK_LIST_NUM; ++i)
{
ppFreeList = m_szFreeList + i;
p = *ppFreeList;
if (NULL != p)
{
*ppFreeList = p->pFreeListLink;
m_pStartFree = (char*)p;
m_pEndFree = m_pStartFree + (i + 1) * ALIGN;
return AllocChunk(nSize, nObjs);
}
}
m_pEndFree = NULL;
return NULL;
}
// 重新分配尺寸為n的內(nèi)存, 其中n是經(jīng)過(guò)字節(jié)對(duì)齊處理的數(shù)
void* CMemPool::Refill(size_t n)
{
// 每個(gè)鏈表每次初始化時(shí)最多LIST_NODE_NUM個(gè)元素
int nObjs = LIST_NODE_NUM;
char* pChunk = AllocChunk(n, nObjs);
Obj** ppFreeList;
Obj* pResult;
Obj *pCurrentObj, *pNextObj;
int i;
// 如果只請(qǐng)求成功了一個(gè)元素, 直接返回之
if (1 == nObjs)
{
return pChunk;
}
// 獲得尺寸n的HASH表地址
ppFreeList = m_szFreeList + GetFreeListIndex(n);
// 獲得請(qǐng)求的內(nèi)存地址
pResult = (Obj*)pChunk;
// 請(qǐng)求了一個(gè)單位內(nèi)存, 減少一個(gè)計(jì)數(shù)
--nObjs;
// 從下一個(gè)單位開(kāi)始將剩余的obj連接起來(lái)
*ppFreeList = pNextObj = (Obj*)(pChunk + n);
// 將剩余的obj連接起來(lái)
for (i = 1; ; ++i)
{
pCurrentObj = pNextObj;
pNextObj = (Obj*)((char*)pNextObj + n);
// 分配完畢, 下一個(gè)節(jié)點(diǎn)為NULL, 退出循環(huán)
if (nObjs == i)
{
pCurrentObj->pFreeListLink = NULL;
break;
}
pCurrentObj->pFreeListLink = pNextObj;
}
return pResult;
}