内存池到底为我们解决了什么问题

      做服务器开发的相信多数都知道内存池,一个普遍认识是内存池是为了消除内存碎片而出现的,而就我的经验来看,其实并不是这样。

      1.内存池主要作用是提高分配回收效率,因为每次new delete都要进入到内核,是一个效率相对较低的操作。

      2.系统中不用内存池,也不会产生大量的内存碎片

      本文主要分析讲解第2点,为什么不会产生大量的内存碎片,提前回答标题,内存池就是为了提高分配效率。

      要否定一个观点,首先要知道这个观点的形成原因,那么普遍认为的碎片的产生,是由于大量的new/delete造成,系统new操作是找到最近的一个足够大的连续空间分配出去,如果某个较小的内存被delete回收到系统,而之后发生的new操作需要更大尺寸的内存,于是较小的内存被弃用成为所谓的内存碎片。

      以上观点要想成立,需要一个前提,那就是程序中每次new出来的内存大小都不一样,且delete比new少。

      这里提出一个概念——有限对象需求

      所谓“有限对象需求”,是程序的一个通性,不管什么程序,同一时刻需要的使用的对象数量与种类都是有限的。

      而支撑内存碎片出现的前提,就是一个“无限对象需求”的程序模型,是实际程序设计中是不存在的,所以说不使用内存池也不会造成大量内存碎片,为什么不是0碎片,碎片还是会有,但是存在时间非常短,很快就会被重新吸收利用。

     

      举个例子来看看有限对象需求下,内存碎片的瞬间产生,瞬间消失的过程

      假设你的程序中需要频繁的通过new产生,然后delete的对象,尺寸从小到大依次存在a,b,c,d,e...等有限数量的类型。

      下面开始无数次new/delete的循环中,看看碎片到底去了哪里。

       可能不很容易懂,先大致浏览下留个印象,下面我会通过简明文字进行说明

       看如下new/delete序列为

       new n次a 以下x次y对象表达为x*y

       new m*b   与n个a相邻

       delete m*a(m<n)  

       new x*c  产生(n-m)*a - q*c(0 <= q <= x)大小的碎片?前次delete空出的的连续内存,可能可以容纳q个c,剩余部分成为碎片?

       大概几秒后发生以下事件,内存碎片自然消失

       new y*d 与x-q个c相邻

       delete (n-m)*a 所有的a被释放

       delete m*b   所有的b被释放

       new x*a   可能全部在一个连续区域,可能分成2部分,1部分是(n-m)*a之前释放的,一部分是(x-(n-m))*a

       delete   xx*c xx<x

       delete y*d

       new x*a

       delete (x-xx)*c    所有的c全部被释放,之前产生的(n-m)*a - q*c(0 <= q <= x)大小的碎片重新恢复成(n-m)*a的连续区域

       new x*a 此x非前面的x,只是个代号,不要做大小等同,重新利用(n-m)*a的连续区域,碎片重新被利用

       好了,所谓的碎片消失了

       上面的过程演示了一个程序中(可以是服务器也可以是游戏client),频繁无序的new/delete操作过程中,内存碎片产生又再在短时间内消失的过程。

       核心原理就是

       碎片产生:n个大对象的new占用了m个小对象所能使用的内存段,导致暂时的内存碎片出现

       碎片消失:n大对象的delete,n*大对象+碎片,重新恢复了m个小对象的大小,可以重新被利用于小对象的new操作。

       这就是为了不用内存池也不会产生内存岁片的原因,除非你的n个大对象永远不delete才会真的产生碎片,但那就不是内存碎片,而是内存泄露了,内存泄露不是内存池能解决的,即使使用内存池,结果也是导致内存池不断变大,最终吃光内存。


       再来看看实际程序开发中的new/delete情况是否符合上面的所说的“有限对象需求”这个特性

       1.不管什么程序,需要new的对象的种类是有限,无限的话,一个对象一个class,你的程序代码就永远没法写完了

       2.同一时刻需要使用的对象数量有限

                对象的需求来源于程序执行的任务的多少,而一个软件系统中,同一时刻任务数量肯定是有限的,前一匹任务执行完毕前,新的任务不会开始,否则系统就该被负荷压跨了。

       以上只是理论分析,下面看程序验证,只要验证系统new是选择最近的足够大的连续空间。

int main(int argc, char* argv[])

{
    int *p = new int;     //断点查看结果,在我的机器上p地址为441830
    char *c = new char; //断点查看结果,在我的机器上c地址为441800
    int *q = new int;    //断点查看结果,在我的机器上q地址为4417d0
    delete p;
    delete c;
    c = new char;         //断点查看结果,在我的机器上c地址为441830,占用了早前p得到的内存块,最近的足够大的连续内存
    p = new int;           //断点查看结果,在我的机器上p地址为441800,占用了c第一new时得到的内存块(第2块内存)

    return 0;
}

      

       以上只是简单测试代码,有兴趣的朋友可以,自己编写一些更复杂的比如带循环的进行测试,欢迎共同研究讨论

       最后如果你发现自己的程序中碎片很多,按照有限对象需求模型的原理——只有当对象没有被delete时才会导致碎片无法恢复,就可以知道一定是内存泄露了。

转载于:https://my.oschina.net/u/732357/blog/88909

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值