C++内存池实现

news/2024/11/16 5:12:40/

1.内存池概念

内存池就和其他的池数据(如线程池)结构类似,由程序维护一个“池”结构来管理程序使用的内存,然后根据需要从内存池中申请使用内存或者向内存池中释放内存,来达到高效管理内存的目的。
在一般的内存管理的库函数中,不管是C中的malloc/free还是C++中的New/delet,都涉及到一个问题:在申请和释放内存时,都需要与操作系统进行交互来完成内存的分配,而在释放时,系统需要对申请的堆内存就行整理,判断free的内存块前后是否有空闲,如果有的话就需要进行合并,因此,直接使用库函数来向系统申请内存会耗费大量的时间,同时也可能产生大量的内存碎片,对系统整体造成压力。
因此,对于需要在短时间内大量申请或者释放小块内存的系统,维护一个内存池来管理内存的分配和回收,在提高系统的效率和并发能力上就很有意义了。
内存池的原理就是,由程序在初始化时一次性向系统申请一块大的内存,然后将其分成多个固定大小的内存块来进行管理,从而避免程序运行时频繁的进行系统调用来减少内存碎片和分配开销。

2.内存池框架

在这里的内存池实现框架中,把内存池分为前端和后端两个部分,由后端维护16个自由链表,在每个链表下挂载管理相同大小的内存块,从8,16,24到128,在申请使用时,当申请的内存小于128字节时,从内存池中分配对应大小的内存块给对象,如果申请的内存大于128字节,则使用malloc从系统中获取。在释放内存时,从内存池申请的内存释放给内存池的管理工具,而从系统申请的内存则释放给系统,由系统进行维护。
<a class=内存池维护的数据结构" />前端使用类模板,通过重新定义一个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

http://www.ppmy.cn/news/1547359.html

相关文章

Java学习Day60:回家!(ElasticStatic)

1.what is ElasticStatic The Elastic Stack, 包括 Elasticsearch、 Kibana、 Beats 和 Logstash&#xff08;也称为 ELK Stack&#xff09;。能够安全可靠地获取任何来源、任何格式的数据&#xff0c;然后实时地对数据进行搜索、分析和可视化。 Elaticsearch&#xff0c;简称…

XML Schema 字符串数据类型

XML Schema 字符串数据类型 1. 概述 XML Schema 是一种用于定义 XML 文档结构和内容的语言。它提供了一种强大的机制来描述 XML 数据的类型、结构和约束。在 XML Schema 中&#xff0c;字符串数据类型是一种基本数据类型&#xff0c;用于表示文本数据。 2. 字符串数据类型 …

【大语言模型】ACL2024论文-10 CSCD-IME: 纠正拼音输入法产生的拼写错误

【大语言模型】ACL2024论文-10 CSCD-IME: 纠正拼音输入法产生的拼写错误 目录 文章目录 【大语言模型】ACL2024论文-10 CSCD-IME: 纠正拼音输入法产生的拼写错误目录摘要研究背景问题与挑战如何解决创新点算法模型1. 错误检测模型2. 伪数据生成模块3. n-gram语言模型过滤4. 多任…

mysql 配置文件 my.cnf 增加 lower_case_table_names = 1 服务启动不了的原因

原因&#xff1a;在MySQL8.0之后的版本&#xff0c;只允许在数据库初始化时指定&#xff0c;之后不允许修改了 mysql 配置文件 my.cnf 增加 lower_case_table_names 1 服务启动不了 报错信息&#xff1a;Job for mysqld.service failed because the control process exited …

ElegantRL:高效、稳定的深度强化学习开源框架

ElegantRL是一个专为大规模并行深度强化学习&#xff08;DRL&#xff09;设计的开源框架&#xff0c;由Yonv1943&#xff08;或AI4Finance-Foundation&#xff09;开发。以下是关于ElegantRL的详细介绍&#xff1a; 一、项目背景与特点 项目名称&#xff1a;ElegantRL&#xf…

「iOS」——知乎日报第三周总结

知乎日报 前言详情页WKWebView的学习无限右滑小菊花控件工具栏 总结 前言 第三周完成了详情页的逻辑&#xff0c;主要写了无限右滑的逻辑&#xff0c;对一些UI控件进行优化。 详情页 WKWebView的学习 WKWebView是是苹果推崇的一个新的类&#xff0c;它用于将一个网页嵌套在软…

【3D Slicer】的小白入门使用指南一

一、3D Slicer认识 3D Slicer是一个开源医学影像分析和可视化平台(本质是TotalSegmentator的软件版)。(补充:TotalSegmentator 是一个用于医学图像分割的开源工具,能够对104种解剖结构进行精确分割。该项目基于深度学习技术,支持CT和MR图像的处理。TotalSegmentator 提供…

lab_2_3_144

lab_2_3_144 lab2主要是32位相对序列号WrappingInt32和64位绝对序列号的转换 q&#xff1a;序列号的作用&#xff1a; a&#xff1a;序列号的主要目的是确保数据的正确传输和顺序&#xff0c;以及在接收方进行数据的重传请求时能够准确地指出需要重传的数据部分 1、唯一性&…