1.内存池概念
内存池就和其他的池数据(如线程池)结构类似,由程序维护一个“池”结构来管理程序使用的内存,然后根据需要从内存池中申请使用内存或者向内存池中释放内存,来达到高效管理内存的目的。
在一般的内存管理的库函数中,不管是C中的malloc/free还是C++中的New/delet,都涉及到一个问题:在申请和释放内存时,都需要与操作系统进行交互来完成内存的分配,而在释放时,系统需要对申请的堆内存就行整理,判断free的内存块前后是否有空闲,如果有的话就需要进行合并,因此,直接使用库函数来向系统申请内存会耗费大量的时间,同时也可能产生大量的内存碎片,对系统整体造成压力。
因此,对于需要在短时间内大量申请或者释放小块内存的系统,维护一个内存池来管理内存的分配和回收,在提高系统的效率和并发能力上就很有意义了。
内存池的原理就是,由程序在初始化时一次性向系统申请一块大的内存,然后将其分成多个固定大小的内存块来进行管理,从而避免程序运行时频繁的进行系统调用来减少内存碎片和分配开销。
2.内存池框架
在这里的内存池实现框架中,把内存池分为前端和后端两个部分,由后端维护16个自由链表,在每个链表下挂载管理相同大小的内存块,从8,16,24到128,在申请使用时,当申请的内存小于128字节时,从内存池中分配对应大小的内存块给对象,如果申请的内存大于128字节,则使用malloc从系统中获取。在释放内存时,从内存池申请的内存释放给内存池的管理工具,而从系统申请的内存则释放给系统,由系统进行维护。
前端使用类模板,通过重新定义一个New类和Delete类来帮助使用者利用内存池管理内存。
3.工具类:Mutex
这里是封装一个锁,来保证在内存池使用时的线程安全。
//Mutex.h//
// Created by crab on 2024/10/28.
//#ifndef MUTEX_H
#define MUTEX_H
#include <pthread.h>class Mutex
{
public://创建锁static Mutex* createNew();//构造与析构函数Mutex();~Mutex();//加锁 解锁void lock();void unlock();//获取锁的对象pthread_mutex_tpthread_mutex_t* get() { return &mMutex; };private:pthread_mutex_t mMutex;};
//Guard对象,用来保证锁与加锁的生命周期一致
class MutexLockGuard
{
public:MutexLockGuard(Mutex* mutex);~MutexLockGuard();private:Mutex* mMutex;};
#endif //MUTEX_H
//Mutex.cpp
//
// Created by crab on 2024/10/28.
//#include "Mutex.h"
#include "New.h"Mutex* Mutex::createNew()
{//return new Mutex();return New<Mutex>::allocate();
}Mutex::Mutex()
{pthread_mutex_init(&mMutex, NULL);
}Mutex::~Mutex()
{pthread_mutex_destroy(&mMutex);
}void Mutex::lock()
{pthread_mutex_lock(&mMutex);
}void Mutex::unlock()
{pthread_mutex_unlock(&mMutex);
}MutexLockGuard::MutexLockGuard(Mutex* mutex) :mMutex(mutex)
{mMutex->lock();
}MutexLockGuard::~MutexLockGuard()
{mMutex->unlock();
}
4.代码概览
4.1内存池后端
//Allocator.h
//
// 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:enum {ALIGN = 8};enum {MAX_BYTES = 128};enum {NFREELISTS = MAX_BYTES / ALIGN};union Obj {union Obj* next;char data[1];};static void* allocate(uint32_t size);static void deallocate(void* p, uint32_t size);private:Allocator() : mStartFree(NULL), mEndFree(NULL), mHeapSize(0){mMutex = new Mutex;memset(mFreeList, 0, sizeof(mFreeList));};~Allocator() {};static Allocator* getInstance();void* alloc(uint32_t size);void dealloc(void* p, uint32_t size);/* 获取指定字节数在自由链表的下标 */uint32_t freelistIndex(uint32_t bytes) {return (((bytes) + ALIGN-1) / ALIGN - 1);}/* 字节对齐 */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:static Allocator* mAllocator;Mutex* mMutex;/* 维护缓存块 */char* mStartFree;char* mEndFree;uint32_t mHeapSize;Obj* mFreeList[NFREELISTS];};
#endif //ALLOCATOR_H
//Allocator.cpp
//
// Created by crab on 2024/10/28.
//#include "Allocator.h"
#include <cstdlib>
#include <iostream>Allocator* Allocator::mAllocator = NULL;void* Allocator::allocate(uint32_t size)
{return getInstance()->alloc(size);
}void Allocator::deallocate(void* p, uint32_t size)
{getInstance()->dealloc(p, size);
}Allocator* Allocator::getInstance()
{if(!mAllocator)mAllocator = new Allocator();return mAllocator;
}void* Allocator::alloc(uint32_t size)
{Obj* result;uint32_t index;MutexLockGuard mutexLockGuard(mMutex);/* 如果分配内存大于 MAX_BYTES,那么就直接通过 malloc 分配 */if(size > MAX_BYTES)return malloc(size);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);}
}
4.2内存池前端
//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 Allocatortemplate <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
5.代码解释
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);//释放指定大小的内存块pstatic 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进行与运算,结果为16uint32_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 Allocatortemplate <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