ImageVerifierCode 换一换
格式:DOCX , 页数:16 ,大小:18.32KB ,
资源ID:82144      下载积分:15 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/82144.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(C++内存池(附源码).docx)为本站会员(wj)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

C++内存池(附源码).docx

1、C+内存池(附源码)C+内存池(附源码)前段时间阅读了Nginx的源码,其对内存效的管理给我留下了深刻的印象,内存管理的核便是内存池。于是想实现个C+版本的内存池,这当然还是STL的内存池最为经典,所以免不了参悟借鉴。内存池的概念早已经是常谈,然把内存池实现的效安全仍是个较艰巨的问题。内存池的原理简单来讲就是次性的向系统申请量的内存,之后再有内存请求的时候,如果内存池的内存能够满请求,就从内存池分配,不必再进系统调,从实现性能提升,多次的内存申请系统调,很容易成内存碎造成内存浪费。池的概念体如此,线程池,进程池出其右。内存池的实现主要解决的问题有: 1. 内存池的块管理 2. 内存的分配和回收

2、 3. 块内存的分配和回收 4. 对象初始化内存池的块管理 这可以直接参考STL的分配器的实现,SGI STL在进内存分配时,默认使了个内存池。这个内存池的内存块从8Byte开始,每递增8Byte都成系列链表管理的内存块,直到128Byte结束。内存块定义为:union MemNode MemNode* _next;char _data1;union每个成员的起始地址都是开头的位置,所以每次仅能使个成员,在链表中由_next指向下个内存块的地址,在分配内存时由_data指向内存地址,长度为1 的数组放在结构体最后个成员位置,可以访问给结构体多分配的地址空间,这种技术叫做柔性数组。这样做的好处减

3、少了对内存块管理时额外的内存损耗。想想我们学习数据结构时实现的链表,都是通过结构体的个成员来指向下个节点的地址,多出了个指针4Byte的内存消耗。参考STL,我们内存块的管理如下图所:有同学要问了,那我要是申请128更的内存怎么办?SGI 这就直接正常的内存申请,还是会有系统调产。因为系统对于程序请求的内存,管理时也会成额外的内存控制数据占内存,这样申请的内存越,额外占的内存例就越。 我们每次申请指定量的内存,然后将内存格式化到块管理的数组链表中。char* res;size_t need_bytes = size * nums;size_t left_bytes = _pool_end -

4、_pool_start;/内存池够if (left_bytes = need_bytes) res = _pool_start;_pool_start += need_bytes;return res; else if (left_bytes = size) nums = left_bytes / size;need_bytes = size * nums;res = _pool_start;_pool_start += need_bytes;return res;size_t bytes_to_get = size * nums;if (!is_large) if (left_bytes 0

5、) MemNode* my_free = _free_listFreeListIndex(left_bytes);(MemNode*)_pool_start)-_next = my_free;_free_listFreeListIndex(size) = (MemNode*)_pool_start; else free(_pool_start);_pool_start = (char*)malloc(bytes_to_get);/内存分配失败if (0 = _pool_start) throw std:exception(There memary is not enough!);_malloc

6、_vec.push_back(_pool_start);_pool_end = _pool_start + bytes_to_get;return ChunkAlloc(size, nums, is_large);将返回的内存添加到块管理队列中my_free = &(_free_listFreeListIndex(size);*my_free = next = (MemNode*)(chunk + size);for (int i = 1; i+) current = next;next = (MemNode*)(char*)next + size);if (nums - 1 = i) cur

7、rent-_next = nullptr;break; else current-_next = next;内存的分配和回收每次从系统申请内存时都通过个辅助函数将内存增到为8的倍数,上层请求内存时寻找最能容纳当前请求的头节点索引/获取size最8的倍数size_t RoundUp(size_t size) return (size + _align - 1) & (_align - 1);/获取容纳当前size的最内存块索引size_t FreeListIndex(size_t size) return (size + _align - 1) / _align - 1;当找到索引位置时,如果内

8、存块不为空,则取出当前内存块,将之后的链表节点向前移动,如果内存不够的话,再次向系统请求新的内存。std:unique_locklock(_mutex);MemNode* my_free = &(_free_listFreeListIndex(sz);MemNode* result = *my_free;if (result = nullptr) void* bytes = ReFill(RoundUp(sz);memset(bytes, 0, sz);return bytes;*my_free = result-_next;memset(result, 0, sz);return resul

9、t;内存回收时与此理相同,通过辅助函数找到索引位置,将内存块放部位置,之前的内存块后移。MemNode* node = (MemNode*)m;MemNode* my_free = &(_free_listFreeListIndex(len);std:unique_locklock(_mutex);node-_next = *my_free;*my_free = node;m = nullptr;块内存的分配和回收通过以上的内存管理,我们以解决块内存的配和回收,但是还可能存在另种需求,类似Nginx内存池有块内存的管理,我们在实际开发中也会到诸如接收发送缓存之类的需求。这添加增加个新的类,以管

10、理块的内存块,且要持动态的增减:通过个vector来管理池中空闲的内存块,需要注意的是析构时要将所有从池中申请的内存还给内存池,不然就需要动释放。申请内存块的命周期管理可以交给。对象初始化与C语实现内存池的不同之处在于,C语可以只负责内存的分配不管内部数据的初始化,因为C语没有对象的概念。但是在C+中,我们不仅仅要负责内存的分配,还要调构造函数负责对象的初始化。家知道C+中的 new操作符,是负责内存申请,是调构造函数实现对象初始化。C+中可以通过可变模板参数来实现任意数量任意参数的函数转发,再辅之std:forward完美转发,即可实现构造函数的调功能。所以我实现的内存池对外提供内存申请的接

11、有三个:/for object. invocation of constructors and destructorstemplateT* PoolNew(Args&. args);templatevoid PoolDelete(T* &c);/for continuous memorytemplateT* PoolMalloc(int size);templatevoid PoolFree(T* &m, int len);/for bulk memory./return one bulk memory nodetemplateT* PoolLargeMalloc();templatevoid

12、 PoolLargeFree(T* &m);这样每次请求和释放都需要调接,C+对这种操作最熟悉不过,我们交给智能指针来管理即可。还有这为什么没有重载new操作符来呢?因为new和delete的重载函数只能是static函数(因为new对象的时候,对象还没有创建),所以内存池的api通过重载new 和delete 实现,看起来很美好,但实际上是不通的。我们要创建内存池的对象,每个内存池的对象管理的都是不同的内存。下看下PoolNew 调构造函数的过程。templateT* CMemaryPool:PoolNew(Args&. args) int sz = sizeof(T);if (sz _ma

13、x_bytes) void* bytes = malloc(sz);T* res = new(bytes) T(std:forward(args).);return res;std:unique_locklock(_mutex);MemNode* my_free = &(_free_listFreeListIndex(sz);MemNode* result = *my_free;if (result = nullptr) void* bytes = ReFill(RoundUp(sz);T* res = new(bytes) T(std:forward(args).);return res;*

14、my_free = result-_next;T* res = new(result) T(std:forward(args).);return res;到这基本上所有的功能都已经实现完毕。但是既然我们持创建内存池的对象,那什么时候释放内存池占有的内存呢? 当然是析构函数中! 但是怎么释放呢? 我们是通过malloc 库函数申请的内存,释放的时候然是去调free释放。但是我们不能通过循环块的数组和链表去释放内存。因为我们申请的时候是整块去申请的,释放的时候只要通过每次申请的头地址去释放即可。所以我在这添加了个辅助的std:vector来存储每次申请内存的地址,释放的时候只遍历这个std:vector即可。/声明std:vector_malloc_vec;/存储_pool_start = (char*)malloc(bytes_to_get);if (0 = _pool_start) throw std:exception(There memary is not enough!);_malloc_vec.push_back(_pool_start);/释放for (auto iter = _malloc_vec.begin(); iter !=

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1