当前位置:   article > 正文

内存池的介绍与经典实现

内存池

在做项目时,学习另一个新的知识点内存池。于是就想着记录一下。说明:本篇文章转载自腾讯云开发者社区:内存池的介绍与经典实现
1.默认内存管理函数的不足:
利用默认的内存管理函数new/delete和malloc/free在堆上分配和释放内存时都会有一些额外的开销。
系统在收到分配一定大小内存的请求时,会根据一定的算法(分配最先找到的不小于请求大小的内存块,或者分配最适用于请求大小的内存给请求者,或者分配最大的空闲块)找到合适的内存空闲块,如果该空闲块过大,还要切割成更小的内存块,然后系统更新内存链表,完成一次内存分配操作。
另外,如果实在多线程的操作下,每一次的内存分配还需要加锁,同样会增加开销。频繁的分配和释放内存都会增加系统的开销。
2.内存池的简介:
2.1 **定义:**内存池(Memory Pool )是一种内存分配方式。使用new/delete和malloc/free来分配内存会产生很多的内存碎片,导致性能降低。
2.2 内存池的优点:
内存池是在真正使用内存之前分配一定数量的、大小相等的内存块留作备用。当有新的内存请求时,就从内存池中分配一块内存块,若内存块不够则继续申请新的内存块。
2.3 内存池的分类:
单就从线程安全角度来看,可以分为单线程内存池和多线程内存池。单线程内存池整个生命周期只有一个线程在访问,因此不需要考虑内互斥的问题。多线程内存池,在访问时有可能被多个线程共享,因此在访问时需要加锁。
2.4 经典内存池的设计
1.先申请一块连续的内存空间,该段内存空间能够容纳一定数量的对象。
2.每个对象连同指向下一个对象的指针一起构成一个内存结点(Memory Node)。各个空闲的内存结点通过指针来构成一个内存结点链表,链表的每一个结点都是一块可供分配的内存空间。
3.某个结点一旦分配出去就链表中删除。
4.一旦释放了该节点,就又从该节点加入到自由空闲结点连链表中。
5.如果一个内存块的被全部分配完了,并且有新的内存分配申请,则再次申请一个新的内存块,并且将该内存块添加到内存块链表中。
上述描述的大致过程如下图:
在这里插入图片描述
如上图所示:浅蓝色代表申请的内存块,蓝色代表内存结点,一个内存块包含有3个内存结点,内存结点由链表管理,如果分配出去则将其从链表中删除。如果释放将其添加回链表的头部。
3.经典内存池的数据结构设计
内存池模板类应该是如下设计:
两个常量:
内存块大小:MemBlockSize;
结点大小:ItemSize;
两个指针变量:
内存块链表头指针:pMemBlockHeader;
空闲内存结头链表头指针:pFreeNodeHeader;
空闲结点结构体:

 // 内存结点
    struct FreeNode
    {
        // 指向下一个内存结点的指针
        FreeNode* pNodeNext;
        // 数据对象
        char data[ObjectSize];
    };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

内存块结构体:

 // 内存块
    struct MemBlock
    {
        // 指向下一个内存块
        MemBlock* pMemNext;
        // 一个内存块中内存结点的数目
        FreeNode data[NumofObjects];
    };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

经典内存池的实现如下:
MemPool.h文件

#pragma once
template<int ObjectSize,int NumofObjects = 20>
class MemPool
{
private:
    // 内存块的大小
    const int m_nMemBlocksSize;
    // 节点的大小
    const int m_nItemSize;

    // 内存结点
    struct FreeNode
    {
        // 指向下一个内存结点的指针
        FreeNode* pNodeNext;
        // 数据对象
        char data[ObjectSize];
    };

    // 内存块
    struct MemBlock
    {
        // 指向下一个内存块
        MemBlock* pMemNext;
        // 一个内存块中内存结点的数目
        FreeNode data[NumofObjects];
    };

    // 链表中指向内存头结点链表的指针
    FreeNode* m_pFreeNodeHeader;
    // 链表中指向内存块头结点的指针
    MemBlock* m_pMemBlockHeader;

public:
    // 构造函数
    MemPool() :m_nItemSize(ObjectSize + sizeof(FreeNode*)),
        m_nMemBlocksSize(sizeof(MemBlock*) + NumofObjects * (ObjectSize + sizeof(FreeNode*)))
    {
        m_pFreeNodeHeader = nullptr;

        m_pMemBlockHeader = nullptr;
    }
    // 析构函数
    ~MemPool()
    {
        MemBlock* ptr;
        while (m_pMemBlockHeader)
        {
            ptr = m_pMemBlockHeader->pMemNext;
            delete m_pMemBlockHeader;
            m_pMemBlockHeader = ptr;
        }
    }
    /// <summary>
    ///  Malloc: 分配内存
    /// </summary>
    /// <returns></returns>
    void* Malloc();
    /// <summary>
    /// Free:释放内存
    /// </summary>
    /// <param name=""></param>
    void Free(void*);
};

// 分配内存
template<int ObjectSize, int NumofObjects>
void* MemPool<ObjectSize,NumofObjects>::Malloc()
{
    // 
    if (m_pFreeNodeHeader == nullptr)
    {
        // 申请内存块
        MemBlock* pNewBlock = new MemBlock();
        // 首先将第下一个内存结点的设置为nullptr
        pNewBlock->data[0].pNodeNext = nullptr;

        for (int i=1;i< NumofObjects;++i)
        {
            // 如果申请内存数超过一个结点
            pNewBlock->data[i].pNodeNext = &pNewBlock->data[i - 1];
        }
        // 将内存头结点指针指向申请内存数目的后一位结点
        m_pFreeNodeHeader = &pNewBlock->data[NumofObjects - 1];
        // 
        pNewBlock->pMemNext = m_pMemBlockHeader;
    }

    void* freeNode = m_pFreeNodeHeader;

    // 指针头结点指向下一个内存结点
    m_pFreeNodeHeader = m_pFreeNodeHeader->pNodeNext;

    return freeNode;
}

// 释放内存
template<int ObjectSize, int NumofObjects>
void MemPool<ObjectSize, NumofObjects>::Free(void* p)
{
    FreeNode* pNode = (FreeNode*)p;

    // 结点链表中的操作,当前要释放结点时,先指向当前头结点
    pNode->pNodeNext = m_pFreeNodeHeader;
    // 最后将头结点当前节点指向当前节点
    m_pFreeNodeHeader = pNode;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107

MemPool.cpp文件

#include <iostream>
#include"MemPool.h"
using namespace std;

class ActualClass
{
public:
    ActualClass()
    {
        m_nNO = m_nCount;
        m_nCount++;
    }

    void Print()
    {
        cout << this << ":";
        cout << "the " << m_nNO << " Object" << endl;
    }
    void* operator new(size_t size);
    void operator delete(void* p);

private:
    static int m_nCount;
    int m_nNO;

};

MemPool<sizeof(ActualClass), 2> mp;

void* ActualClass::operator new(size_t size)
{
    return mp.Malloc();
}
void ActualClass::operator delete(void* p)
{
    mp.Free(p);
}

int ActualClass::m_nCount = 0;

int main()
{
    // 定义一个指针对象
    ActualClass* p1 = NULL;
    p1 = new ActualClass();
    p1->Print();

    ActualClass* p2 = new ActualClass();
    p2->Print();
    delete p1;

    p1 = new ActualClass();
    p1->Print();

    ActualClass* p3 = new ActualClass();
    p3->Print();

    delete p1;
    delete p2;
    delete p3;
    system("pause");
    std::cout << "Hello World!\n";
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

上面的程序裕兴结果如下:在这里插入图片描述
说明:如结果所示:在分配完p2时释放p1,然后再次为p1分配内存时,这个新的内存恰好是上一次释放的内存。如果把它p1改成p3,则p3的地址和p1原来的地址相同。上面结果中p3的地址是新分配的内存块中的地址。
欢迎大家一起交流学习.
参考:
内存池介绍与经典内存池的实现

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/292459
推荐阅读
相关标签
  

闽ICP备14008679号