這個內(nèi)存池之前已經(jīng)介紹過了,在
這里這次整理自己的服務器公共庫,沒有忘記這個好東西,算法沒有改變,但是有兩個變化:
1) 我認為這個模塊對于一個服務器而言, 應該是一個單件, 所以它繼承自前面說過的singleton基類.
2) 加入了線程鎖, 同樣是可以配置的, 因為我基本不寫多線程的服務器, 不過, 還是把接口保留在那里吧, 如果需要可以打開以支持多線程.
mempool.h:
/********************************************************************
created: 2008/08/03
filename: mempool.h
author: Lichuang
purpose: 仿SGI STL內(nèi)存池的實現(xiàn)
*********************************************************************/
#ifndef __MEM_POOL_H__
#define __MEM_POOL_H__
#include "singleton.h"
#include "threadmutex.h"
#include <stdio.h>
// 字節(jié)對齊數(shù)
#define ALIGN 512
// 最大BLOCK的尺寸
#define MAX_BLOCK_SIZE 20 * 1024
// HASH表的數(shù)量
#define BLOCK_LIST_NUM (MAX_BLOCK_SIZE) / (ALIGN)
// HASH表中每次初始化時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)存池的實現(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)
{
// 如果之前沒有分配, 或者已經(jīng)分配完畢了, 就調(diào)用refill函數(shù)重新分配
// 需要注意的是, 傳入refill的參數(shù)是經(jīng)過對齊處理的
pResult = (Obj*)Refill(RoundUp(nSize));
}
else
{
// 否則就更新該HASH表的LIST頭節(jié)點指向下一個LIST的節(jié)點, 當分配完畢時, 頭結點為NULL
*ppFreeList = pResult->pFreeListLink;
}
THREAD_UNLOCK;
return pResult;
}
void* CMemPool::Reallocate(void* p, size_t nOldSize, size_t nNewSize)
{
void* pResult;
size_t nCopySize;
// 如果超過內(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)存尺寸在對齊之后相同, 那么直接返回
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ù)量, 也就是說至少還可以分配一個單位內(nèi)存
// 計算出最多可以分配多少塊單位內(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)存, 將它放到對應的HASH-LIST頭部
if (0 < nBytesLeft)
{
Obj** ppFreeList = m_szFreeList + GetFreeListIndex(nBytesLeft);
((Obj*)m_pStartFree)->pFreeListLink = *ppFreeList;
*ppFreeList = (Obj*)m_pStartFree;
}
// 需要獲取的內(nèi)存, 注意第一次分配都要兩倍于total_bytes的大小
// 同時要加上原有的heap_size / 4的對齊值
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);
}
// 下面是獲取不成功的處理
.
// 從下一個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)過字節(jié)對齊處理的數(shù)
void* CMemPool::Refill(size_t n)
{
// 每個鏈表每次初始化時最多LIST_NODE_NUM個元素
int nObjs = LIST_NODE_NUM;
char* pChunk = AllocChunk(n, nObjs);
Obj** ppFreeList;
Obj* pResult;
Obj *pCurrentObj, *pNextObj;
int i;
// 如果只請求成功了一個元素, 直接返回之
if (1 == nObjs)
{
return pChunk;
}
// 獲得尺寸n的HASH表地址
ppFreeList = m_szFreeList + GetFreeListIndex(n);
// 獲得請求的內(nèi)存地址
pResult = (Obj*)pChunk;
// 請求了一個單位內(nèi)存, 減少一個計數(shù)
--nObjs;
// 從下一個單位開始將剩余的obj連接起來
*ppFreeList = pNextObj = (Obj*)(pChunk + n);
// 將剩余的obj連接起來
for (i = 1; ; ++i)
{
pCurrentObj = pNextObj;
pNextObj = (Obj*)((char*)pNextObj + n);
// 分配完畢, 下一個節(jié)點為NULL, 退出循環(huán)
if (nObjs == i)
{
pCurrentObj->pFreeListLink = NULL;
break;
}
pCurrentObj->pFreeListLink = pNextObj;
}
return pResult;
}