c++内存池

参考文章:C++ 内存池介绍与经典内存池的实现-CSDN博客

为了解决频繁使用new、malloc等函数造成的内存碎片问题。

1. 内存池的分类

从线程安全的角度来分,可分为单线程线程池和多线程线程池。单线程线程池整个生命周期被一个线程使用,不用考虑互斥访问的问题,多线程内存池可能被多个线程共享,每次分配释放内存时需要加锁。

就可分配内存单元大小来分,可分为固定内存池和可变内存池。固定内存池是指每次分配的内存单元大小是固定不变的;可变内存池每次分配的内存单元大小是可变的,效率相对于固定内存池会低一些。

2. 内存池技术

2.1 经典内存池

用于分配大量大小相同的对象,分配释放速度快。

实现过程:

1)先申请一块连续的内存空间,该段内存空间能够容纳一定数量的对象;

2)每个对象连同一个指向下一个对象的指针一起构成一个内存节点(Memory Node)。各个空闲的内存节点通过指针形成一个链表,链表的每一个内存节点都是一块可供分配的内存空间;

3)某个内存节点一旦分配出去,从空闲内存节点链表中去除;

4)一旦释放了某个内存节点的空间,又将该节点重新加入空闲内存节点链表(头插);

5)如果一个内存块的所有内存节点分配完毕,若程序继续申请新的对象空间,则会再次申请一个内存块来容纳新的对象。新申请的内存块会加入内存块链表中。

 

struct FreeNode {
    FreeNode* pNext;
    char data[ObjectSize];
};
struct MemBlock
{
    MemBlock *pNext;
    FreeNode data[NumofObjects];
};
#include <iostream>
using namespace std;

template<int ObjectSize, int NumofObjects = 20>
class MemPool {
private:
    //空闲节点结构体
    struct FreeNode{
        FreeNode* pNext;
        char data[ObjectSize];
    };

    //内存块结构体
    struct MemBlock{
        MemBlock* pNext;
        FreeNode data[NumofObjects];
    };

    FreeNode* freeNodeHeader;
    MemBlock* memBlockHeader;

public:
    MemPool() {
        freeNodeHeader = NULL;
        memBlockHeader = NULL;
    }

    ~MemPool() {
        MemBlock* ptr;
        while (memBlockHeader)
        {
            ptr = memBlockHeader->pNext;
            delete memBlockHeader;
            memBlockHeader = ptr;
        }
    }
    void* malloc();
    void free(void*);
};

// 分配空闲的结点。
template<int ObjectSize, int NumofObjects>
void* MemPool<ObjectSize, NumofObjects>::malloc(){
    //无空闲节点,申请新内存块
    if (freeNodeHeader == NULL){
        MemBlock* newBlock = new MemBlock;
        newBlock->pNext = NULL;

        freeNodeHeader=&newBlock->data[0];     //设置内存块的第一个节点为空闲节点链表的首节点
        //将内存块的其它节点串起来
        for (int i = 1; i < NumofObjects; ++i) {
            newBlock->data[i - 1].pNext = &newBlock->data[i];
        }
        newBlock->data[NumofObjects - 1].pNext=NULL;

        // 首次申请内存块
        if (memBlockHeader == NULL) {
            memBlockHeader = newBlock;
        } else {
            // 将新内存块加入到内存块链表。
            newBlock->pNext = memBlockHeader;
            memBlockHeader = newBlock;
        }
    }
    // 返回空节点闲链表的第一个节点。
    void* freeNode = freeNodeHeader;
    freeNodeHeader = freeNodeHeader->pNext;
    return freeNode;
}

// 释放已经分配的结点。
template<int ObjectSize, int NumofObjects>
void MemPool<ObjectSize, NumofObjects>::free(void* p) {
    FreeNode* pNode = (FreeNode*)p;
    pNode->pNext = freeNodeHeader;    //将释放的节点插入空闲节点头部
    freeNodeHeader = pNode;
}

class ActualClass {
    static int count;
    int No;

public:
    ActualClass() {
        No = count;
        count++;
    }

    void print() {
        cout << this << ": ";
        cout << "the " << No << "th object" << endl;
    }

    void* operator new(size_t size);
    void operator delete(void* p);
};

// 定义内存池对象
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::count = 0;

int main() {
    ActualClass* 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;
}

主要开销在没有空闲节点,申请新的内存块时,需要把内存块内部节点初始化。

正常情况下申请和分配的复杂度都是o(1)。

有以下缺点:1. 不能申请对象数组;2. 内存块个数只能增大不能减少;3. 没有考虑多线程安全。

待续。。。

posted @ 2025-03-22 10:56  横渡大海的神仙鱼  阅读(172)  评论(0)    收藏  举报