1. 程式人生 > >一個簡化版本的記憶體池實現

一個簡化版本的記憶體池實現

最近寫的一個程式中需要頻繁的申請和釋放零碎的記憶體塊,這些記憶體塊的大小卻只有簡單的幾種。如果直接使用系統呼叫malloc/freenew/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 <stddef.h>
#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 <iostream>
#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<T>::MemPool2()
{
    m_addr = NULL;
    m_freeList = NULL;
    m_blockSize = sizeof(T);
    m_NBlocks = 0;
    m_NFree = 0;
}
template < typename T >
MemPool2<T>::~MemPool2()
{
    if(m_addr != NULL)
    {
        delete [] m_addr;
    }
}
template < typename T >
int MemPool2<T>::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<T>::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<T>::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 <iostream>
#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<IMAGE_INFO> 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;
}