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