作服务器开发的相信多数都知道内存池,一个广泛认识是内存池是为了消除内存碎片而出现的,而就个人经验来看,其实并非这样。 服务器
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时才会致使碎片没法恢复,就能够知道必定是内存泄露了。