C++内存池实现-5.代码解释

时间:2024-11-15 14:53:19

1.内存池后端

//
// Created by crab on 2024/10/28.
//

#ifndef ALLOCATOR_H
#define ALLOCATOR_H

#include <cstdint>
#include <cstdlib>
#include <cstring>

#include "Mutex.h"


//内存池
//单例类,通过getInstance获取唯一实例
class Allocator
{
public:
	//ALIGN:内存块的对齐单位常量
    enum {ALIGN = 8};
    //Max_Bytes:内存块的最大字节数
    enum {MAX_BYTES = 128};
    //NFREELISTS:*链表数量
    enum {NFREELISTS = MAX_BYTES / ALIGN};
	
	//一个union联合体,用来作为内存块节点的数据结构。 
    union Obj {
        union Obj* next;
        char data[1];
    };
	//根据请求的size的分配内存
    static void* allocate(uint32_t size);
	//释放指定大小的内存块p
    static void deallocate(void* p, uint32_t size);

private:
	//构造函数,初始化内存池的起始和结束指针mStartFree和mEndFree,堆大小mHeapSize,分配一个Mutex对象管理线程安全
    Allocator() : mStartFree(NULL), mEndFree(NULL), mHeapSize(0)
    {
        mMutex = new Mutex;
        memset(mFreeList, 0, sizeof(mFreeList));
    };

    ~Allocator() {

    };
	
	//静态方法用来获取Allocator的唯一实例
    static Allocator* getInstance();
	
	//内存分配
    void* alloc(uint32_t size);
    //内存释放
    void dealloc(void* p, uint32_t size);

    /* 获取指定字节数在*链表的下标 */
    //快速找到该大小的链表头,如:16字节的内存:(16 + 8 - 1)/ 8 - 1= 1 
    uint32_t freelistIndex(uint32_t bytes) {
        return (((bytes) + ALIGN-1) / ALIGN - 1);
    }

    /* 字节对齐 */
	//将给定的字节数向上取整为8的倍数,实现快速对齐 
	//eg: 14字节: (14+8-1)~(8-1)=16 : ~(8-1) 7的二进制取反:11111000, 然后将21与11111000进行与运算,结果为16
    uint32_t roundup(uint32_t bytes) {
        return (((bytes) + ALIGN-1) & ~(ALIGN - 1));
    }
	//补充指定大小的内存块
    void *refill(uint32_t bytes);
    //从堆上分配多个内存块
    char* chunkAlloc(uint32_t size, int& nobjs);

private:
	//Allocator的静态实例
    static Allocator* mAllocator;
	//锁
    Mutex* mMutex;

    /* 维护缓存块 */
    char* mStartFree;
    char* mEndFree;
    uint32_t mHeapSize;
	
	//指针数组,用来维护内存块链表
    Obj* mFreeList[NFREELISTS];

};
#endif //ALLOCATOR_H

//
// Created by crab on 2024/10/28.
//

#include "Allocator.h"
#include <cstdlib>
#include <iostream>

Allocator* Allocator::mAllocator = NULL;

void* Allocator::allocate(uint32_t size)
{
	//通过Allocator的Instance调用alloc分配size大小的内存块
    return getInstance()->alloc(size);
}

void Allocator::deallocate(void* p, uint32_t size)
{
	//delloc释放
    getInstance()->dealloc(p, size);
}

Allocator* Allocator::getInstance()
{
    if(!mAllocator)
        mAllocator = new Allocator();

    return mAllocator;
}

//分配内存,如果需要的内存大于MAX_BYTES,直接用malloc分配
void* Allocator::alloc(uint32_t size)
{
    Obj* result;
    uint32_t index;
	//加锁,确保线程安全
    MutexLockGuard mutexLockGuard(mMutex);

    /* 如果分配内存大于 MAX_BYTES,那么就直接通过 malloc 分配 */
    if(size > MAX_BYTES)
        return malloc(size);
	//获取内存块在链表数组中的位置,然后从mFreeList中获取对应链表的上的内存块
    index = freelistIndex(size);
    result = mFreeList[index];

    /* 如果没有找到则重新分配内存 */
    if(!result)
    {
        void* r = refill(roundup(size));
        return r;
    }

    /* 找到了就从链表中删除内存块 */
    mFreeList[index] = result->next;

    return result;
}

void Allocator::dealloc(void* p, uint32_t size)
{
    Obj* obj = (Obj*)p;
    uint32_t index;

    MutexLockGuard mutexLockGuard(mMutex);

    /* 如果释放内存大于 MAX_BYTES,那么就直接通过 free 释放 */
    if(size > MAX_BYTES)
        free(p);

    index = freelistIndex(size); //获取该大小在freelist的下标

    /* 将内存块添加进链表中 */
    obj->next = mFreeList[index];
    mFreeList[index] = obj;
}

/* 重新分配内存 */
void* Allocator::refill(uint32_t bytes)
{
    int nobjs = 20;
    char* chunk = chunkAlloc(bytes, nobjs); //分配内存
    Obj* result;
    Obj* currentObj;
    Obj* nextObj;
    int i;
    uint32_t index;

    /* 如果只有一个节点,那么直接放回,不需要处理剩余内存 */
    if(1 == nobjs)
        return chunk;

    result = (Obj*)chunk;
    index = freelistIndex(bytes);
    mFreeList[index] = nextObj = (Obj*)(chunk + bytes);

    /* 将剩余内存连成链表 */
    for(i = 1; ; ++i)
    {
        currentObj = nextObj;
        nextObj = (Obj*)((char*)nextObj + bytes);

        if(nobjs-1 == i) //最后一个节点
        {
            currentObj->next = 0;
            break;
        }
        else
        {
            currentObj->next = nextObj;
        }
    }

    return result;
}

char* Allocator::chunkAlloc(uint32_t size, int& nobjs)
{
    char* result;
    uint32_t totalBytes = size * nobjs; //总共需求的内存
    uint32_t bytesLeft = mEndFree - mStartFree; //缓存块中剩余的内存大小

    if(bytesLeft > totalBytes) //如果缓存块的内存满足需求,则直接从缓存块中获取内存
    {
        result = mStartFree;
        mStartFree += totalBytes;
        return result;
    }
    else if(bytesLeft > size) //如果缓存块剩余大小大于一个节点的大小,则尽可能返回多个节点
    {
        nobjs = bytesLeft / size;
        totalBytes = size * nobjs;
        result = mStartFree;
        mStartFree += totalBytes;
        return result;
    }
    else
    {
        uint32_t bytesToGet = 2 * totalBytes + roundup(mHeapSize >> 4); //至少两倍增长

        if(bytesLeft > 0) //如果缓存块还剩余内存,那么它肯定可以插入到某个节点中
        {
            uint32_t index = freelistIndex(bytesLeft);
            ((Obj*)(mStartFree))->next = mFreeList[index];
            mFreeList[index] = (Obj*)mStartFree;
        }

        /* 重新申请内存 */
        mStartFree = (char*)malloc(bytesToGet);

        mHeapSize += bytesToGet;
        mEndFree = mStartFree + bytesToGet;

        /* 递归调用chunkAlloc,重新分配 */
        return chunkAlloc(size, nobjs);
    }
}
//Construct.h
// Created by crab on 2024/10/28.
//

#ifndef CONSTRUCT_H
#define CONSTRUCT_H

#include <new>

//在特定内存位置上构造或销毁对象,与内存池连用

template <class T>
inline void destroy(T* pointer)
{
    pointer->~T();
}

template <class T>
inline void construct(T* p)
{
    new (p) T();
}

template <class T, class T1>
inline void construct(T* p, const T1& a1)
{
    new (p) T(a1);
}

template <class T, class T1, class T2>
inline void construct(T* p, const T1& a1, const T2& a2)
{
    new (p) T(a1, a2);
}

template <class T, class T1, class T2, class T3>
inline void construct(T* p, const T1& a1, const T2& a2, const T3& a3)
{
    new (p) T(a1, a2, a3);
}

template <class T, class T1, class T2, class T3, class T4>
inline void construct(T* p, const T1& a1, const T2& a2, const T3& a3, const T4& a4)
{
    new (p) T(a1, a2, a3, a4);
}

#endif //CONSTRUCT_H

//New.h
// Created by crab on 2024/10/28.
//

#ifndef NEW_H
#define NEW_H

#include "Allocator.h"
#include "Construct.h"

#define     ALLOCATOR       Allocator

template <class T>
class New
{
public:
    typedef     T           Value;
    typedef     T*          Point;
    typedef     T&          Ref;
    typedef     ALLOCATOR   Alloc;

public:
    static Point allocate() {
        Point obj = (Point)Alloc::allocate(sizeof(Value));
        construct(obj);
        return obj;
    }

    template <class T1>
    static Point allocate(const T1& a1) {
        Point obj = (Point)Alloc::allocate(sizeof(Value));
        construct(obj, a1);
        return obj;
    }

    template <class T1, class T2>
    static Point allocate(const T1& a1, const T2& a2) {
        Point obj = (Point)Alloc::allocate(sizeof(Value));
        construct(obj, a1, a2);
        return obj;
    }

    template <class T1, class T2, class T3>
    static Point allocate(const T1& a1, const T2& a2, const T3& a3) {
        Point obj = (Point)Alloc::allocate(sizeof(Value));
        construct(obj, a1, a2, a3);
        return obj;
    }

    template <class T1, class T2, class T3, class T4>
    static Point allocate(const T1& a1, const T2& a2, const T3& a3, const T4& a4) {
        Point obj = (Point)Alloc::allocate(sizeof(Value));
        construct(obj, a1, a2, a3, a4);
        return obj;
    }
};

class Delete
{
public:
    typedef     ALLOCATOR   Alloc;

    template <class T1>
    static void release(T1* point) {
        destroy(point);
        Alloc::deallocate(point, sizeof(T1));
    }

};

#endif //NEW_H