内存池概述

原文链接:https://blog.csdn.net/xiaohaizi77881314/article/details/11668057


总结:
1-使用malloc()方式容易造成一系列问题,如使用越界,内存泄漏、内存碎片等
2- 为了解决上述问题,使用内存池方式。简单来说就是先申请一大块内存(内存池),这一大块内存可包含一系列基本单元不同的块。
例:我一次性申请了一大块内存,基本单元32字节的100块,128字节的50块,512字节的25块,1k的10块,将这些内存块使用链表的方式串联起来。如果后面有内存使用需求,从内存池中申请。比如我需要64字节的内存,那我就从32字节的内存链中申请两块,使用完放回到32字节的内存链。
上面的实现途径,很好的避免了内存碎片等问题,管理内存很珍贵啊。
——————————————————————————————————————————————
通常情况下,内存的分配方式有3种方式:
1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。
——————————————————————————————————————————————
简单malloc()缺陷
使用内存比较容易犯的一些错误。其实大家可不要小看这些错误,很多时候这些错误的产生,对于产品使用者的影响是巨大的。
1- 内存未分配成功,我们就错误的使用了它。
这种情况的发生。就通常和我们没有养成良好的编程习惯或者经验不丰富有关。常用解决办法是,在使用内存之前检查指针是否为NULL。如果指针p是函数的参数,那么在函数的入口处用if(p!=NULL)进行检查。如果我们在函数中使用malloc或new来申请内存,在使用前必须使用if(p!=NULL)判断后,再进行使用。
2- 内存分配虽然成功,但是尚未初始化就引用它。
犯这种错误主要有两个起因:一是没有初始化的观念;二是误以为内存的缺省初值全为零,导致引用初值错误(例如数组)。
内存的缺省初值究竟是什么并没有统一的标准,尽管有些时候为零值,我们宁可信其无不可信其有。所以无论用何种方式创建数组,都别忘了赋初值,即便是赋零值也不可省略,不要嫌麻烦。
3-  内存分配成功并且已经初始化,但操作越过了内存的边界。
这种情况的产生经常会发生在数组下标的错误使用和对象指针的错误内存拷贝
4- 忘记了内存释放,造成内存泄露。
对于这种错误的产生原因可就多种多样了,而最常见犯错误的原因,还是大家没有一个良好的编程习惯或者对于指针使用理解不深刻造成。。

注意:
1.使用指针之前,首先必须检查指针指向内存的有效性。防止使用NULL指针,造成程序崩溃。
2.分配内存成功后,最好要对于内存赋初值。防止将未被初始化的内存作为右值使用,造成程序的错误。
3.避免数组下标或者指针的地址越界,特别要当心发生“多1”、“少1”和内存拷贝的操作。
4.动态内存的申请与释放必须配对,防止内存泄漏。同时不能够将new和free或者malloc和delete进行错误配对。因为这样产生的问题将是难以预料的。
5.用free或delete释放了内存之后,立即将指针设置为NULL,杜绝产生“野指针”。
6.合理的使用栈内存,并且需要关心局部内存对象的生命周期.从而无错高效的使用
————————————————————————————————————————————————
使用内存池的必要性
如果我们使用传统的内存分配方式来进行程序设计,情况将会如下所示:
使用的时候向系统申请,使用后就归还给系统。也正是我们所说的new/delete和malloc/free这种方式。
服务器应用程序开启一段时间之后,我们的系统使用中内存和空闲内存将会呈现如下方式分布: 
这其中将会出现许多内存碎片,由于内存碎片的大量存在,我们服务器性能也会随之降低。并且我们还必须承担由于内存使用不当造成系统不稳定或者内存大量泄露的风险。
既然传统的内存编程方式在服务器程序开发和应用过程中有这样的一些潜在弊端。那么我们使用方式来改进我们的服务器应用程序性能和增强系统稳定性呢?
大家不妨尝试使用,我接下来大家要的内容: 对象内存池技术.

所谓的对象内存池技术设计过程如下:
首先为某种对象预先生成若干个空闲对象,并且使用对象管理类进行管理。应用程序在需要使用此对象时,即向管理对象申请空闲对象.管理对象即检视对象内存池,如果发现存在未使用空闲对象,即分配给申请者。如果发现已无空闲对象,可自行扩充对象内存池,并且满足申请对象的需求,也可以直接返回NULL,表明对象申请失败。在程序获取对象并且使用后,想释放此对象资源时。继续想管理对象提出申请释放对象,管理对象接受到释放对象后将其再次放入对象池,成为可使用对象。
——————————————————————————————————————————————
内存池的实现原理
内存池使用目标(优势)
1- 对象内存池管理对象具有广泛的通用性,也就是说能够满足应用程序生成各个不同的对象池,例如:Player对象池、Monster对象池、NPC对象池。
2- 产生对象的速度一定要快于直接使用new/delete或者malloc/free方式很多倍。
3- 对象池容量具有可扩展性和纠错能力。也就是说在无空闲对象时,管理对象类能够自动生成一批新的空闲对象供上层使用,同时能够正确指出目前所使用对象是否为合法对象池对象。
4- 对象的申请和释放,必须具备多线程安全性。也就是说在服务器程序通过各个不同线程同时访问对象池管理时,能够保证合法获取和释放池对象。

解决方案1:(单链表实现)
第一步,分配模板对象数组,并且用指针保存。
第二步,建立一单链表管理类,将已经分配成功的数组对象,分配到蛋链表中,同时设置表头和表尾指针。
第三步,从对象池中申请对象,首先检测链表中是否存在可使用空闲对象。如果没有可返回NULL,也可以先锁定扩充链表(保存扩展对象数组指针)。然后返回可使用对象给用户。
第四步,释放对象池对象时,首先将表尾指针指向被释放对象,接下来被回收对象为此链表表尾。完成释放过程。
 最后,内存释放,delete数组指针。
图例演示如下:

解决方案2:(双链表实现)
为了能够使我们建立的对象池能够在应用程序中通用,我们考虑使用模板template<class OBJ>来生成我们的class CObjectPool.
为了能够快速获取对象,我们采用双向链表的方式来建立我们的对象池。对象获取从当前链表头开始进行,对象释放直接加到链表尾。操作过程中需要使用一附加指针对象表明当前可使用对象位置。若此指针为NULL,表明已无可使用空闲对象。
为了生成一个一定容量的对象池,我们可以通过模板的方式也可以通过初始化Init方式来生成初始对象池。在申请过程中无空闲对象,需要向系统重新一定数目对象,并且按照顺序加到链表尾。实现对象池的可扩充性。
为了保证多线程安全,我们在对象申请和释放过程中加入Lock进行锁定,保证每次只有一个线程操作对象池。


 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值