|
这个内存池之前已经介绍过了,在 这里这次整理自己的服务器公共库,没有忘记这个好东西,算法没有改变,但是有两个变化: 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; }
|