内存池的简单实现

原创
2022-11-22
4663
1

当我们频繁的申请内存(new、malloc) 和 释放内存(delete、free) 时,会产生内存碎片,而且申请和释放内存也会增加时间的消耗。内存池就是为了解决该问题,提高效率产生的。

这里使用分段分段连续的内存,来存储多次申请和释放的内存。

  • 内存单元: 定义的内存最小单元
  • 内存块: 一块连续的内存,可以存放多个内存单元
  • 内存池: 用链表的形式管理多个内存块

1. 内存单元

下面是内存单元部分定义:

template<typename T>
class MenoryUnit
{
public:
    T unitData;
    MenoryUnit* pNext = nullptr;
};
  • unitData 表示要存储的单元。
  • pNext 指向下一个要赋值的单元。

2. 内存块

template<typename T>
class MemoryBlock
{
public:
    void* data(void) {
        return (MemoryBlock*)m_pData + 1;
    }

    // 创建内存块
    static MemoryBlock* createMemory(MemoryBlock* pHead, int unitSize, int unitCount) {
        int size = sizeof(MemoryBlock) + unitSize * unitCount;
        MemoryBlock* pBlock = (MemoryBlock*)new char[size];
        memset(pBlock, 0, size);
        pBlock->m_pNext = pHead;
        pBlock->m_pData = pBlock;

        return pBlock;
    }

    // 销毁内存块
    void destoryMemoryBlock(void) {
        MemoryBlock* pBlock = this;
        while (pBlock) {
            MemoryBlock *tempBlock = pBlock;
            pBlock = pBlock->m_pNext;

            // 销毁内存块
            delete[] tempBlock->m_pData;
        }
    }

    friend class MemoryPool<T>;
private:
    MemoryBlock* m_pNext = nullptr;
    void *m_pData = nullptr;
};

内存块首部位置保存内存块的信息,这里仅保存了链表下一个节点指针m_pNext和整个的数据指针 m_pData,剩下的数据为要存储内存单元的数据。

  • 函数 createMemory 用于创建内存块,需要指定头内存块的指针,内存单元的大小和内存单元的个数。
  • 函数 destoryMemoryBlock 释放内存块,释放时会释放掉以它为链首的后继链的内存块资源。
  • 函数 data 用于获取内存单元块的首地址。

3. 内存池

template<typename T>
class MemoryPool
{
public:
    MemoryPool() {}
    ~MemoryPool() {
        // 销毁内存块
        m_pBlockHead->destoryMemoryBlock();
    }
public:
    // 创建一个对象
    T* createObject(void) {
        if (m_pFreeUnit == nullptr)
        {
            // 创建内存块
            m_pBlockHead = MemoryBlock<T>::createMemory(m_pBlockHead, sizeof(MenoryUnit<T>), m_nPerBlockUnitSize);
            MenoryUnit<T>* pUnit = (MenoryUnit<T>*)m_pBlockHead->data();
            m_pFreeUnit = pUnit;

            // 为内存块中所有元素单元空间赋值下一个将要赋值的内容
            for (int i = 0; i < m_nPerBlockUnitSize - 1; ++i) {
                (pUnit + i)->pNext = pUnit + i + 1;
            }
        }
        // 设置下一个被添加的元素的位置
        MenoryUnit<T>* pDestObject = m_pFreeUnit;
        m_pFreeUnit = m_pFreeUnit->pNext;

        // 使用placement new 为内存区域赋值
        ::new(pDestObject) T();
        return (T*)&(pDestObject->unitData);
    }

    // 销毁内存对象
    void destoryObject(T* pUnit) {
        if (m_pBlockHead == nullptr)
            return;

        // 循环遍历该元素
        MemoryBlock<T> *pTempBlock = m_pBlockHead;
        while (pTempBlock){
            for (int i = 0; i < m_nPerBlockUnitSize; ++i) {
                MenoryUnit<T> *pTempUnit = (MenoryUnit<T> *)pTempBlock->data() + i;
                if (&(pTempUnit->unitData) == pUnit)
                {
                    // 设置为下一个空位置
                    pTempUnit->pNext = m_pFreeUnit;
                    m_pFreeUnit = pTempUnit;

                    pUnit->~T();// 执行析构函数
                    return;
                }
            }
            pTempBlock = m_pBlockHead->m_pNext;
        }
    }

private:
    MemoryBlock<T>* m_pBlockHead = nullptr;       // 内存块链表头指针
    int m_nPerBlockUnitSize = 10;                 // 每个内存块中元素单元的个数
    MenoryUnit<T> *m_pFreeUnit = nullptr;	  // 用来存放下一个添加元素的位置
};

内存池中 有创建元素的函数 createObject 和 删除元素的函数 destoryObject,这里删除不是释放内存,而是将该块内存的状态标记为未被使用的状态。

  • m_pBlockHead: 存储内存块头指针
  • m_nPerBlockUnitSize: 存储每个内存块中元素的个数
  • m_pFreeUnit: 存储将要添加元素的位置地址。

在创建元素时,为了能够初始化类中的内容,使用了 placement new
关于 new和delete 可参考文章 C++中的new和delete


下面是测试代码:

class TestObject
{
public:
    TestObject() {
        std::cout << "Created TestObject_" << m_nCount << std::endl;
        m_nNumber = m_nCount++;
    }

    ~TestObject() {
        std::cout << "Deleted TestObject_" << m_nNumber << std::endl;
    }

    void print(void) {
        std::cout << "Object Info: m_nNumber " << m_nNumber << std::endl;
    }

    static int m_nCount;
    int m_nNumber = 1;
};

int TestObject::m_nCount = 1;

int main(int argc, char** argv) {

    MemoryPool<TestObject> memPool;

    std::vector<TestObject*> vector;
    // 创建对象
    for (int i = 0; i < 20; ++i) {
        TestObject* o = memPool.createObject();
        o->print();
        std::cout << "-------------------------" << std::endl;
        vector.push_back(o);
    }
    // 销毁对象
    for (auto iter = vector.begin(); iter != vector.end(); ++iter) {
        memPool.destoryObject(*iter);
    }

    system("pause");
    return 0;
}

测试结果打印输出:
Created TestObject_1
Object Info: m_nNumber 1
-------------------------
Created TestObject_2
Object Info: m_nNumber 2
-------------------------
Created TestObject_3
Object Info: m_nNumber 3
-------------------------
Created TestObject_4
Object Info: m_nNumber 4
-------------------------
Created TestObject_5
Object Info: m_nNumber 5
-------------------------
Created TestObject_6
Object Info: m_nNumber 6
-------------------------
Created TestObject_7
Object Info: m_nNumber 7
-------------------------
Created TestObject_8
Object Info: m_nNumber 8
-------------------------
Created TestObject_9
Object Info: m_nNumber 9
-------------------------
Created TestObject_10
Object Info: m_nNumber 10
-------------------------
Created TestObject_11
Object Info: m_nNumber 11
-------------------------
Created TestObject_12
Object Info: m_nNumber 12
-------------------------
Created TestObject_13
Object Info: m_nNumber 13
-------------------------
Created TestObject_14
Object Info: m_nNumber 14
-------------------------
Created TestObject_15
Object Info: m_nNumber 15
-------------------------
Created TestObject_16
Object Info: m_nNumber 16
-------------------------
Created TestObject_17
Object Info: m_nNumber 17
-------------------------
Created TestObject_18
Object Info: m_nNumber 18
-------------------------
Created TestObject_19
Object Info: m_nNumber 19
-------------------------
Created TestObject_20
Object Info: m_nNumber 20
-------------------------
Deleted TestObject_1
Deleted TestObject_2
Deleted TestObject_3
Deleted TestObject_4
Deleted TestObject_5
Deleted TestObject_6
Deleted TestObject_7
Deleted TestObject_8
Deleted TestObject_9
Deleted TestObject_10
Deleted TestObject_11
Deleted TestObject_12
Deleted TestObject_13
Deleted TestObject_14
Deleted TestObject_15
Deleted TestObject_16
Deleted TestObject_17
Deleted TestObject_18
Deleted TestObject_19
Deleted TestObject_20

不会飞的纸飞机
扫一扫二维码,了解我的更多动态。

下一篇文章:c++11学习笔记(1)-__func__预定义标识符