用C++编写高效稳定的软件,有效的内存管理通常是绕不开的一个题目,尤其是对于复杂庞大的商业软件。一方面,内存泄露、指针越界的bug查找即使是C++老鸟都会头大。另一方面,内存碎片的积累会造成系统性能下降甚至崩溃。我本人是做视频分析算法的,算法运行中每帧都会有大量的申请释放内存操作,并且顺序和跨度不一,由此造成的内存碎片问题不容忽视。一个可行的解决方案是自己overload operator new和delete函数,在其中对内存进行分配、释放、重用、和其他管理。某牛曾经说过,“我工作过的公司里,就没有不自己overload new和delete的”。好吧,我们现在也这么干一回。
首先,我们先实现一个内存池。泄露和越界的自动查找功能我们先放一边,当然代码里也会先保留一些结构或字段给它们用。内存池的思想很简单,一个内存块不用了,不要把它释放给系统,我们自己留下,通常是放在一个link list中,即构成了所谓的池。当程序有新的内存申请时,我们从池里拿一个符合尺寸要求的内存块给它。当然,如果池是空的,或者里面的内存块都太小不符合申请要求,就从系统里申请。这么做的好处是什么先,从系统里分配会有不小的时间开销,从池里取会节省不少的时间;其次,避免了内存碎片;最后,在某些应用情况下(比如web服务器连接),可以采用内存池集中释放的机制,可能的内存泄露消饵于无形。
那么问题来了,我如果申请一个4字节的char数组,池中只有1个4K的内存块,难道把这个4K的内存给这个数组用,岂不是有99.9%的是浪费了同学说这简单,把4K内存块的头上4个返回给申请的程序,剩下的还放在池里。但是,这样的后果可能是随着你申请和释放的次数增加,原先的大块的4K内存分成了无数支离破碎无法合并的小块。这…这…这不就是内存碎片么又重新干了一遍系统干的事同学说了,可以这样,4字节的申请只允许在池里找4字节的内存块,多的不给。可…可…可谁知道会有多少种不同的尺寸申请啊么多别的尺寸的内存块就晾在那用不上,到头还得从系统申请/p>
这样,我们限定一下尺寸的种类。只允许出现16、32、64、128、256、512、1024、2048这些大小的内存块。如果要申请12字节的怎么办它个16的,不管是从池里拿还是从系统申请。那么超过2048的怎么办于我搞视频算法的来说,那基本就是图像了,我就从系统里给它分配个实际尺寸的,用完也不放到池里,释放给系统。什么,没解决问题些东东基本都是固定大小的数据结构,不是还有对象池这种存在么定了内存池,对象池不要太难哦。
C同学又说了,“可是这样好像还是很浪费唉。比如我要申请一个2K的数组,池里明明有了两个1K的块,难道还非得从系统中申请个2K的来”其实如果池里的两个1K的内存紧挨着,那还是有办法的。我们再把A同学的方法捡回来,每次我们从系统中申请的都是4K大小的内存,就像系统的页的概念。当有一个具体的内存申请过来时,比如1个32字节的申请,我们把页里还没被分出去的空余地盘分32个字节给这个申请,同时空余地盘的指针往后移32个字节。如果页的空余地盘不够了,申请一个新的页。
之后释放这个内存块进池时,首先判断它能否和对应页的空余地盘合并成一个更大的空余块。行则最好,不行才放入对应于32字节的那个内存块的链表。下次再来个32字节的申请时,先在这个链表里拿,没有再去页里拿。
对应于C同学的忧虑,如果池里的两个1K块可以和页的尾巴合并成一个大于2K的块,那么这个2K的申请池里就能满足了。可是如果两个1K块隔开了呢还用问,mission impossible了呗。
下面我们来实现一下
上面这个是内存块前缀的定义。前缀里的pPrev和pNext两个指针用来维持一个双向链表。这个链表可以是某种尺寸的内存块池,也可以是大内存块(>2048)的链表。pPage指向这个内存块对应的页面。后缀顾名思义,放在分配的内存块的最后面。有嘛用东东不能合并到前缀里想看,页尾巴找前面那个内存块的前缀怎么找知道该往前移多少个字节啊/p>
上面这个是内存块后缀的定义。nActualSize用于日后的内存越界的检查。比如申请12字节的数组A,分配了16字节,如果没有保存这个12字节的大小信息,那么你无法知道A[12]、A[13]是否合法。
上面这个是页的结构定义。
需要注意的是,这几个结构定义都有_declspec(align(16)) ,并且由于申请内存块的尺寸也弄成了16的倍数,这确保了每个内存块的起始地址是16的倍数。这有什么用先,SSE优化对数据的操作是128位的,读写内存最好以16字节为单位。另外,有析构函数的类数组会把申请内存的尺寸加4个字节,并让数组指针指向分配到的内存的第4个字节,而头4个字节是数组大小。保证了内存16字节对齐后,我们就能知道某个数组指针是否有析构。干吗用什么情况后面会介绍。
下一章具体实现
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!