序言一、为什么要使用内存池技术呢
  1. 减少new、delete次数,减少运行时间;
  2. 避免内存碎片。
1、效率2、内存碎片

更多linux内核视频教程文档资料免费领取后台私信【内核】自行获取.

内存入门基础知识13(全网最详细的内存池技术的原理与实现)(1)

二、原理三、具体实现

内存入门基础知识13(全网最详细的内存池技术的原理与实现)(2)

void* MemoryBlock::operator new(size_t, int nUnitSize,int nUnitAmount ) { return ::operator new( sizeof(MemoryBlock) nUnitSize * nUnitAmount ); }

MemoryBlock::MemoryBlock( int nUnitSize,int nUnitAmount ) : nSize (nUnitAmount * nUnitSize), nFree (nUnitAmount - 1), //构造的时候,就已将第一个单元分配出去了,所以减一 nFirst (1), //同上 pNext (NULL) { //初始化数组链表,将每个分配单元的下一个分配单元的序号写在当前单元的前两个字节中 char* pData = aData; //最后一个位置不用写入 for( int i = 1; i < nSize - 1; i ) { (*(USHORT*)pData) = i; pData = nUnitSize; } }

四、使用

void CTest::operator delete( void* pTest ) { Pool.Free(pTest); } void* CTest::operator new(size_t) { return (CTest*)Pool.Alloc(); }

五、代码

#include <stdlib.h> #include <wtypes.h> #define MEMPOOL_ALIGNMENT 8 //对齐长度 //内存块,每个内存块管理一大块内存,包括许多分配单元 class MemoryBlock { public: MemoryBlock (int nUnitSize,int nUnitAmount); ~MemoryBlock(){}; static void* operator new (size_t,int nUnitSize,int nUnitAmount); static void operator delete (void* ,int nUnitSize,int nUnitAmount){}; static void operator delete (void* pBlock); int nSize; //该内存块的大小,以字节为单位 int nFree; //该内存块还有多少可分配的单元 int nFirst; //当前可用单元的序号,从0开始 MemoryBlock* pNext; //指向下一个内存块 char aData[1]; //用于标记分配单元开始的位置,分配单元从aData的位置开始 }; class MemoryPool { public: MemoryPool (int _nUnitSize, int _nGrowSize = 1024, int _nInitSzie = 256); ~MemoryPool(); void* Alloc(); void Free(void* pFree); private: int nInitSize; //初始大小 int nGrowSize; //增长大小 int nUnitSize; //分配单元大小 MemoryBlock* pBlock; //内存块链表 };

#include "MemoryPool.h" MemoryBlock::MemoryBlock( int nUnitSize,int nUnitAmount ) : nSize (nUnitAmount * nUnitSize), nFree (nUnitAmount - 1), //构造的时候,就已将第一个单元分配出去了,所以减一 nFirst (1), //同上 pNext (NULL) { //初始化数组链表,将每个分配单元的下一个分配单元的序号写在当前单元的前两个字节中 char* pData = aData; //最后一个位置不用写入 for( int i = 1; i < nSize - 1; i ) { (*(USHORT*)pData) = i; pData = nUnitSize; } } void* MemoryBlock::operator new(size_t, int nUnitSize,int nUnitAmount ) { return ::operator new( sizeof(MemoryBlock) nUnitSize * nUnitAmount ); } void MemoryBlock::operator delete( void* pBlock) { ::operator delete(pBlock); } MemoryPool::MemoryPool( int _nUnitSize, int _nGrowSize /*= 1024*/, int _nInitSzie /*= 256*/ ) { nInitSize = _nInitSzie; nGrowSize = _nGrowSize; pBlock = NULL; if(_nUnitSize > 4) nUnitSize = (_nUnitSize (MEMPOOL_ALIGNMENT - 1)) & ~(MEMPOOL_ALIGNMENT - 1); else if( _nUnitSize < 2) nUnitSize = 2; else nUnitSize = 4; } MemoryPool::~MemoryPool() { MemoryBlock* pMyBlock = pBlock; while( pMyBlock != NULL) { pMyBlock = pMyBlock->pNext; delete(pMyBlock); } } void* MemoryPool::Alloc() { if( NULL == pBlock) { //首次生成MemoryBlock,new带参数,new了一个MemoryBlock类 pBlock = (MemoryBlock*)new(nUnitSize,nInitSize) MemoryBlock(nUnitSize,nUnitSize); return (void*)pBlock->aData; } //找到符合条件的内存块 MemoryBlock* pMyBlock = pBlock; while( pMyBlock != NULL && 0 == pMyBlock->nFree ) pMyBlock = pMyBlock->pNext; if( pMyBlock != NULL) { //找到了,进行分配 char* pFree = pMyBlock->aData pMyBlock->nFirst * nUnitSize; pMyBlock->nFirst = *((USHORT*)pFree); pMyBlock->nFree--; return (void*)pFree; } else { //没有找到,说明原来的内存块都满了,要再次分配 if( 0 == nGrowSize) return NULL; pMyBlock = (MemoryBlock*)new(nUnitSize,nGrowSize) MemoryBlock(nUnitSize,nGrowSize); if( NULL == pMyBlock) return NULL; //进行一次插入操作 pMyBlock->pNext = pBlock; pBlock = pMyBlock; return (void*)pMyBlock->aData; } } void MemoryPool::Free( void* pFree ) { //找到p所在的内存块 MemoryBlock* pMyBlock = pBlock; MemoryBlock* PreBlock = NULL; while ( pMyBlock != NULL && ( pBlock->aData > pFree || pMyBlock->aData pMyBlock->nSize)) { PreBlock = pMyBlock; pMyBlock = pMyBlock->pNext; } if( NULL != pMyBlock ) //该内存在本内存池中pMyBlock所指向的内存块中 { //Step1 修改数组链表 *((USHORT*)pFree) = pMyBlock->nFirst; pMyBlock->nFirst = (USHORT)((ULONG)pFree - (ULONG)pMyBlock->aData) / nUnitSize; pMyBlock->nFree ; //Step2 判断是否需要向OS释放内存 if( pMyBlock->nSize == pMyBlock->nFree * nUnitSize ) { //在链表中删除该block delete(pMyBlock); } else { //将该block插入到队首 PreBlock = pMyBlock->pNext; pMyBlock->pNext = pBlock; pBlock = pMyBlock; } } }

#include <stdio.h> #include "MemoryPool.h" class CTest { public: CTest(){data1 = data2 = 0;}; ~CTest(){}; void* operator new (size_t); void operator delete(void* pTest); public: static MemoryPool Pool; int data1; int data2; }; void CTest::operator delete( void* pTest ) { Pool.Free(pTest); } void* CTest::operator new(size_t) { return (CTest*)Pool.Alloc(); } MemoryPool CTest::Pool(sizeof(CTest)); int main() { CTest* pTest = new CTest; printf("%d",pTest->data2); }

六、问题
  1. 重载new操作符时,编译器要求是第一个参数必须是size_t,返回值必须是void*;free的第一个参数必须是void*.
  2. 一般要在类的成员中重载new操作符,而不要重载全局的new操作符。
  3. 一个类中要是重载了一个new操作符,一定要有一个相应类型的delete操作符,可以什么都不干,但必须有,否则在构造函数失败时,找不到对应的delete函数。

static void* operator new (size_t,int nUnitSize,int nUnitAmount); static void operator delete (void* ,int nUnitSize,int nUnitAmount){};

4. 带参数的new操作符

pBlock = (MemoryBlock*)new(nUnitSize,nInitSize) MemoryBlock(nUnitSize,nUnitSize);

5. 如果在类的内部不能进行静态成员的定义的话,可以只在内部进行声明,在外部定义:

MemoryPool CTest::Pool(sizeof(CTest));

内存入门基础知识13(全网最详细的内存池技术的原理与实现)(3)

,