最近写的一个程序中需要频繁的申请和释放零碎的内存块,这些内存块的大小却只有简单的几种。如果直接使用系统调用malloc/free、new/delete进行内存分配和释放,则效率很低。程序运行时间长了会产生大量的内存碎片。想起uC/OS-II 里面提供了个内存分配单元,正好满足我的需要。就把里面相关的代码扒了出来。写成了一个内存池的类。
这个内存池的功能非常的简单,初始化时分配一大块内存,然后将各个小内存块串成一个单项链表。每次分配内存块时从链表的头上去取一个内存块。回收内存块时也是将内存块插到链表的开头处。
这个类的结构如下:
#ifndef MEMPOOL_H
#define MEMPOOL_H
#define MEM_NO_ERR 0
#define MEM_INVALID_ADDR 1
#define MEM_INVALID_BLKS 2
#define MEM_INVALID_SIZE 3
#define MEM_INVALID_PART 4
#define MEM_INVALID_PBLK 5
#define MEM_FULL 6
class MemPool
{
public:
MemPool();
~MemPool();
int create (int nblocks, unsigned int blocksize);
void* get( void );
int release ( void *pblk );
int blocks( void ) const {return m_memNBlks;};
int frees( void ) const {return m_memNFree;};
private:
char *m_memAddr; /* Pointer to beginning of memory partition */
char *m_memFreeList; /* Pointer to list of free memory blocks */
int m_memBlkSize; /* Size (in bytes) of each block of memory */
int m_memNBlks; /* Total number of blocks in this partition */
int m_memNFree; /* Number of memory blocks remaining in this */
};
#endif
create 函数初始化内存池。主要的工作就是分配内存,然后将内存块串起来形成一个链表。因为要用指针形成链表,因此要求内存块的大小至少要能容纳一个指针。
get 函数获得一个内存块,如果没有剩余的内存块了,就返回 null。
release 函数回收内存块。
blocks 函数返回内存池总共有多少内存块。
frees 函数返回内存池还剩多少剩余的内存块。
代码实现如下:
#include
#include "MemPool.h"
MemPool::MemPool()
{
m_memAddr = NULL;
m_memFreeList = NULL;
m_memBlkSize = 0;
m_memNBlks = 0;
m_memNFree = 0;
}
MemPool::~MemPool()
{
if(m_memAddr != NULL)
{
delete [] m_memAddr;
}
}
int MemPool::create ( int nblks, unsigned int blksize )
{
if( m_memAddr != NULL )
{
delete [] m_memAddr;
}
m_memAddr = new char[nblks * blksize];
if ( m_memAddr == NULL )
{
return MEM_INVALID_ADDR;
}
if ( nblks < 2 )
{
/* Must have at least 2 blocks per partition */
return MEM_INVALID_BLKS;
}
if ( blksize < sizeof(void *) )
{
/* Must contain space for at least a pointer */
return MEM_INVALID_SIZE;
}
void ** p = (void **)m_memAddr;
char *pblk = m_memAddr + blksize;
for (int i = 0; i < (nblks - 1); i++)
{
*p = (void *) pblk;
p = (void **) pblk;
pblk = pblk + blksize;
}
*p = (void *)0;
m_memFreeList = m_memAddr;
m_memNBlks = nblks;
m_memNFree = nblks;
return MEM_NO_ERR;
}
void * MemPool::get( void )
{
void *pblk;
if (m_memNFree > 0)
{
/* See if there are any free memory blocks */
pblk = m_memFreeList; /* Yes, point to next free memory block */
m_memFreeList = (char *) *(void **)pblk; /* Adjust pointer to new free list */
m_memNFree--;/* One less memory block in this partition */
return (pblk); /* Return memory block to caller */
}
return ((void *)0);
}
int MemPool::release ( void *pblk )
{
if (pblk == (void *)0)
{
/* Must release a valid block */
return (MEM_INVALID_PBLK);
}
if (m_memNFree >= m_memNBlks)
{
/* Make sure all blocks not already returned */
return (MEM_FULL);
}
/* Insert released block into free block list */
*(void **)pblk = m_memFreeList;
m_memFreeList = (char *) pblk;
m_memNFree++; /* One more memory block in this partition */
return (MEM_NO_ERR); /* Notify caller that memory block was released */
}
#include
#include "MemPool.h"
using namespace std;
typedef struct
{
unsigned char *ImageData;
int SizeX;
int SizeY;
int ImageID;
double Timestamp;
double TransferTime;
unsigned int PacketCount;
}IMAGE_INFO;
int main()
{
IMAGE_INFO * p[15];
MemPool mem;
mem.create(15, sizeof(IMAGE_INFO));
for(int i = 0; i < 10; i++)
{
p[i] = (IMAGE_INFO *)mem.get();
cout << "p[" << i << "] addr = " << p[i] << endl;
p[i]->SizeX = i;
p[i]->SizeY = i;
}
for(int i = 1; i < 10; i++)
{
cout << "p[" << i << "]->SizeX = " << p[i]->SizeX << endl;
mem.release(p[i]);
}
cout << "mem.blocks()" << mem.blocks() << endl;
cout << "mem.frees()" << mem.frees() << endl;
return 0;
}
这个测试程序的运行结果如下:
p[0] addr = 0xac1358
p[1] addr = 0xac1380
p[2] addr = 0xac13a8
p[3] addr = 0xac13d0
p[4] addr = 0xac13f8
p[5] addr = 0xac1420
p[6] addr = 0xac1448
p[7] addr = 0xac1470
p[8] addr = 0xac1498
p[9] addr = 0xac14c0
p[1]->SizeX = 1
p[2]->SizeX = 2
p[3]->SizeX = 3
p[4]->SizeX = 4
p[5]->SizeX = 5
p[6]->SizeX = 6
p[7]->SizeX = 7
p[8]->SizeX = 8
p[9]->SizeX = 9
mem.blocks()15
mem.frees()14
最后多说一句,如果程序中多个线程要访问同一个内存池,那个需要给 get 和 release 函数加锁。
另外,这个代码其实可以用C++的类模版来实现。下面是用了类模版技术的一个实现:
#ifndef MEMPOOL2_H_INCLUDED
#define MEMPOOL2_H_INCLUDED
#define MEM_NO_ERR 0
#define MEM_INVALID_ADDR 1
#define MEM_INVALID_BLKS 2
#define MEM_INVALID_SIZE 3
#define MEM_INVALID_PART 4
#define MEM_INVALID_PBLK 5
#define MEM_FULL 6
template < typename T >
class MemPool2
{
public:
MemPool2();
~MemPool2();
int create (int nblocks);
T* get( void );
int release ( T *pblk );
int blocks( void ) const {return m_NBlocks;};
int frees( void ) const {return m_NFree;};
private:
T *m_addr; /* Pointer to beginning of memory partition */
void *m_freeList; /* Pointer to list of free memory blocks */
int m_blockSize; /* Size (in bytes) of each block of memory */
int m_NBlocks; /* Total number of blocks in this partition */
int m_NFree; /* Number of memory blocks remaining in this */
};
template < typename T >
MemPool2::MemPool2()
{
m_addr = NULL;
m_freeList = NULL;
m_blockSize = sizeof(T);
m_NBlocks = 0;
m_NFree = 0;
}
template < typename T >
MemPool2::~MemPool2()
{
if(m_addr != NULL)
{
delete [] m_addr;
}
}
template < typename T >
int MemPool2::create ( int nblocks )
{
if( m_addr != NULL )
{
delete [] m_addr;
}
m_addr = new T[nblocks];
if ( m_addr == NULL )
{
return MEM_INVALID_ADDR;
}
if ( nblocks < 2 )
{
/* Must have at least 2 blocks per partition */
return MEM_INVALID_BLKS;
}
if ( sizeof(T) < sizeof(void *) )
{
/* Must contain space for at least a pointer */
return MEM_INVALID_SIZE;
}
void ** p = (void **)m_addr;
T *pblock = m_addr;
for (int i = 0; i < (nblocks - 1); i++)
{
pblock ++;
*p = (void *) pblock;
p = (void **) pblock;
}
*p = (void *)0;
m_freeList = m_addr;
m_NBlocks = nblocks;
m_NFree = nblocks;
return MEM_NO_ERR;
}
template < typename T >
T * MemPool2::get( void )
{
T *pblk;
if (m_NFree > 0)
{
/* See if there are any free memory blocks */
pblk = (T *) m_freeList; /* Yes, point to next free memory block */
m_freeList = (void *) *(void **)pblk; /* Adjust pointer to new free list */
m_NFree--;/* One less memory block in this partition */
return (pblk); /* Return memory block to caller */
}
return ((T *)0);
}
template < typename T >
int MemPool2::release ( T *pblock )
{
if (pblock == NULL)
{
/* Must release a valid block */
return (MEM_INVALID_PBLK);
}
if (m_NFree >= m_NBlocks)
{
/* Make sure all blocks not already returned */
return (MEM_FULL);
}
/* Insert released block into free block list */
*(void **)pblock = m_freeList;
m_freeList = (void *) pblock;
m_NFree++; /* One more memory block in this partition */
return (MEM_NO_ERR); /* Notify caller that memory block was released */
}
#endif // MEMPOOL2_H_INCLUDED
#include
#include "MemPool2.h"
using namespace std;
typedef struct
{
unsigned char *ImageData;
int SizeX;
int SizeY;
int ImageID;
double Timestamp;
double TransferTime;
unsigned int PacketCount;
}IMAGE_INFO;
int main()
{
IMAGE_INFO * p[15] = {0};
MemPool2 mem;
mem.create(5);
for(int i = 0; i < 7; i++)
{
p[i] = mem.get();
if(p[i] != NULL)
{
cout << "p[" << i << "] addr = " << p[i] << endl;
p[i]->SizeX = i;
p[i]->SizeY = i;
}
}
for(int i = 0; i < 9; i++)
{
if(p[i] != NULL)
{
cout << "p[" << i << "]->SizeX = " << p[i]->SizeX << endl;
}
mem.release(p[i]);
}
cout << "mem.blocks()" << mem.blocks() << endl;
cout << "mem.frees()" << mem.frees() << endl;
return 0;
}