为什么要用内存池
C++程序默认的内存管理(new,delete,malloc,free)会频繁地在堆上分配和释放内存,导致性能的损失,产生大量的内存碎片,降低内存的利用率。默认的内存管理因为被设计的比较通用,所以在性能上并不能做到极致。
因此,很多时候需要根据业务需求设计专用内存管理器,便于针对特定数据结构和使用场合的内存管理,比如:内存池。
内存池原理
内存池的思想是,在真正使用内存之前,预先申请分配一定数量、大小预设的内存块留作备用。当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存,当内存释放后就回归到内存块留作后续的复用,使得内存使用效率得到提升,一般也不会产生不可控制的内存碎片。
内存池设计
算法原理:
预申请一个内存区chunk,将内存中按照对象大小划分成多个内存块block
维持一个空闲内存块链表,通过指针相连,标记头指针为第一个空闲块
每次新申请一个对象的空间,则将该内存块从空闲链表中去除,更新空闲链表头指针
每次释放一个对象的空间,则重新将该内存块加到空闲链表头
如果一个内存区占满了,则新开辟一个内存区,维持一个内存区的链表,同指针相连,头指针指向最新的内存区,新的内存块从该区内重新划分和申请
如图所示:
免费学习地址:C/C++Linux服务器开发/后台架构师【零声教育】-学习视频教程-腾讯课堂
【文章福利】:小编整理了一些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!~点击加入(832218493需要自取)
内存池实现
memory_pool.hppifndef_MEMORY_POOL_H_define_MEMORY_POOL_H_includeinclude template<size_tBlockSize,size_tBlockNum =10>class MemoryPool{public: MemoryPool() {std::lock_guard<std::mutex> lk(mtx);// avoid race condition// init empty memory pointer free_block_head = NULL; mem_chunk_head = NULL; }~MemoryPool() {std::lock_guard<std::mutex> lk(mtx);// avoid race condition// destruct automatically MemChunk* p; while (mem_chunk_head) { p = mem_chunk_head->next; delete mem_chunk_head; mem_chunk_head = p; } }void*allocate(){std::lock_guard<std::mutex> lk(mtx);// avoid race condition// allocate one object memory// if no free block in current chunk, should create new chunk if (!free_block_head) { // malloc mem chunk MemChunk* new_chunk = new MemChunk; new_chunk->next = NULL;// set this chunks first block as free block head free_block_head = &(new_chunk->blocks[0]);// link the new chunks all blocks for (int i = 1; i < BlockNum; i++) new_chunk->blocks[i - 1].next = &(new_chunk->blocks[i]); new_chunk->blocks[BlockNum - 1].next = NULL; // final block next is NULL if (!mem_chunk_head) mem_chunk_head = new_chunk; else { // add new chunk to chunk list mem_chunk_head->next = new_chunk; mem_chunk_head = new_chunk; } }// allocate the current free block to the object void* object_block = free_block_head; free_block_head = free_block_head->next;returnobject_block; }void*allocate(size_tsize){std::lock_guard<std::mutex> lk(array_mtx);// avoid race condition for continuous memory// calculate objects num int n = size / BlockSize;// allocate n objects in continuous memory //FIXME:make sure n > 0 void* p = allocate();for(inti =1; i < n; i++) allocate();returnp; }voiddeallocate(void* p){std::lock_guard<std::mutex> lk(mtx);// avoid race condition// free object memory FreeBlock* block = static_cast(p); block->next = free_block_head; // insert the free block to head free_block_head = block; } private:// free node block, every block size exactly can contain one object struct FreeBlock { unsigned char data[BlockSize]; FreeBlock* next; };FreeBlock* free_block_head;// memory chunk, every chunk contains blocks number with fixed BlockNum struct MemChunk { FreeBlock blocks[BlockNum]; MemChunk* next; };MemChunk* mem_chunk_head;// thread safe related std::mutex mtx; std::mutex array_mtx;};endif// !_MEMORY_POOL_H_
main.cpp
includeinclude "memory_pool.hpp" classMyObject{public: MyObject(intx): data(x) {//std::cout << "contruct object" << std::endl; }~MyObject() {//std::cout << "destruct object" << std::endl; }intdata;// override new and delete to use memory pool void* operator new(size_t size); void operator delete(void* p); void* operator new[](size_t size); void operator delete[](void* p);};// define memory pool with block size as class sizeMemoryPool gMemPool; void* MyObject::operatornew(size_tsize){//std::cout << "new object space" << std::endl; return gMemPool.allocate();}voidMyObject::operatordelete(void* p){//std::cout << "free object space" << std::endl; gMemPool.deallocate(p);}void* MyObject::operatornew[](size_tsize){//TODO:not supported continuous memoery pool for now //return gMemPool.allocate(size); return NULL;}void MyObject::operator delete[](void* p){ //TODO:not supported continuous memoery pool for now //gMemPool.deallocate(p);}intmain(intargc,char* argv[]){ MyObject* p1 =newMyObject(1);std::cout<<"p1 "<< p1 <<" "<< p1->data<<std::endl;
MyObject* p2 =newMyObject(2);std::cout<<"p2 "<< p2 <<" "<< p2->data <<std::endl;deletep2;
MyObject* p3 =newMyObject(3);std::cout<<"p3 "<< p3 <<" "<< p3->data <<std::endl;
MyObject* p4 =newMyObject(4);std::cout<<"p4 "<< p4 <<" "<< p4->data <<std::endl;
MyObject* p5 =newMyObject(5);std::cout<<"p5 "<< p5 <<" "<< p5->data <<std::endl;
MyObject* p6 =newMyObject(6);std::cout<<"p6 "<< p6 <<" "<< p6->data <<std::endl;deletep1;deletep2;//delete p3; delete p4; delete p5; delete p6;getchar();return0;}
运行结果
p100000174BEDE0440 1p2 00000174BEDE0450 2p3 00000174BEDE0450 3p4 00000174BEDE0460 4p5 00000174BEDD5310 5p6 00000174BEDD53206
可以看到内存地址是连续,并且回收一个节点后,依然有序地开辟内存
对象先开辟内存再构造,先析构再释放内存
注意
在内存分配和释放的环节需要加锁来保证线程安全
还没有实现对象数组的分配和释放
声明:本文部分素材转载自互联网,如有侵权立即删除 。
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!
7. 如遇到加密压缩包,请使用WINRAR解压,如遇到无法解压的请联系管理员!
8. 精力有限,不少源码未能详细测试(解密),不能分辨部分源码是病毒还是误报,所以没有进行任何修改,大家使用前请进行甄别
丞旭猿论坛
暂无评论内容