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

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

      1.内存池主要做用是提升分配回收效率,由于每次new delete都要进入到内核,是一个效率相对较低的操做。 测试

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

      本文主要分析讲解第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时才会致使碎片没法恢复,就能够知道必定是内存泄露了。

相关文章
相关标签/搜索