STLPort、Loki之内存分配器
一个好的内存分配器(allocator),对于服务器的性能是至关重要的,vc版STL、STLPort、Loki、ACE之类的库都带了内存分配器,但是它们的实现方法、效率都有所不同,在别人的代码里也看了不少别人写的内存分配器,自己也写过一个内存分配器,它们或多或少都有一些不满足要求、不够灵活或者效率还可以改善之类的问题,现在在这里对这些内存分配器做一个比较,并为实现一个完善的allocator提供一点帮助。
VC所带的STL中的allocator:最简单的内存分配器,简单的调用new来分配内存,跟直接使用new没什么两样,不过STL中的allocator本身就是一个模板参数,缺省的allocator只是一个默认实现而已,可以仿照allocator实现自己的内存分配,并用于list、map之类的容器中
STLPort中的allocator:提供了两种内存分配器,一种为简单的调用new,另一种采用预分配内存池的方式来分配内存,这种方式和loki中提供的分配器大致相同,但有一些小的区别,它们之间的细节差别,下面会详细的分析到。候捷翻译的《STL源码剖析》一书中详细介绍了StlPort中的内存分配器实现的原理
ACE:ace的内存分配器提供了多种接口,易用性好,但ace代码结构本身比较庞大,分析起来比较复杂,对于个人实现自己的内存分配器用处不大
Loki:loki是一个短小精悍的库,这里只考虑其中的内存分配器,在候捷翻译的《C++设计新思维》中也论述了loki内存分配器的相关内容。但出书时loki版本较低,现在loki已经发展到0.1.3版,就内存分配器而言,跟书中所述已经有些出入,接口也变得更加丰富,下面着重分析loki 0.1.3中的内存分配器。
loki的内存分配器一共分为4个层次,从底层到上层依次是Chunk、FixedAllocator、SmallObjAllocator、SmallObj:
Chunk : 管理一个大内存块,其中包含若干相同大小的内存分配单元
FixedAllocator : 固定大小的内存分配器,管理若干Chunk,负责新Chunk的建立以及内存释放时Chunk内存的释放
SmallObjAllocator : 不定大小的内存分配器,管理不同大小的FixedAllocator,可分配不同大小不同的内存块
SmallObj :重载new和delete操作符,使用SmallObjAllocator来分配内存,所有小对象的基类
ACE和loki的分配方案大致上都相同,最重要的一个差别是ACE缓存所有已经分配过的内存,一直都不释放,而loki的作法是当某一个chunk的内存全部被应用程序归还时,向系统归还这个chunk的内存。很明显,如果两者的效率相同的话,显然后者更优越,可惜世上没有免费的午餐,loki要达到这个目的,就必须要做以下的运算:
1,归还内存的时候,根据要归还的内存地址和大小,找到该内存所在的chunk,loki的作法是保存最后一次归还操作的chunk作为高速缓存,下一次归还内存时首先判断该内存是否属于此chunk,是的话直接归还,否则要作一个线性搜索,loki 0.1.3中采用的方法是根据此chunk的位置作向前向后搜索,目的是加大搜索的命中率
2,申请内存的时候,也是同样,分配器保存上次分配内存时的chunk,下次分配的时候直接拿该chunk来分配,如果该chunk的内存已经分配完毕,同样需要做一次线性搜索
鉴于当前大多数应用服务器的瓶颈并不在普通内存上,似乎ACE的作法要更好一点,即用单链表保存当前已归还的内存块,如果该链表已空,则向操作系统申请虚拟内存,释放内存时则加入到链表中,这种方法的效率应该是最高的,但是缺点是可能某个瞬间系统内存消耗过大而且永不释放