最近分析学习了一个内存池程序源代码。关于这个内存池的作者和源代码,可以从这个链接中得到(http://256.com/sources/mpool/)。下面打算对我这些日子的理解做个总结,把学到的记下来。
我只能给出一个概述,对一些太过于细节的就不做记录了。这里说到的内存池特指上面链接中的内存池。
关于性能
原本一直以为C语言自带的malloc或者calloc的速度很慢,经过测试之后才发现是在不断重复大量的malloc/free后,malloc才会变得很慢,可能就是传说中的有大量内存碎片影响了速度吧。如果只是少量的malloc/free,malloc还是很快的,内存池就显得特别慢了。内存池适用于这样的程序:大量重复malloc和free,所申请内存的大小变化不大。这里说的重复malloc和free是说内存的使用上存在类似于malloc,free,malloc,free,malloc,free ……这样的序列。
其实所谓的内存池就是一种内存管理策略。如用什么数据结构组织内存,每次申请多少内存,如何释放内存等等。标准库的malloc等函数已经实现了很不错的内存策略,貌似标准库使用了红黑树或散列表的方式来管理进程堆。不同的内存策略有不同的优劣。不同程序可能需要不同的内存策略。为了减少内存碎片,提高内存利用率,有些时候需要比较特殊的内存策略,那就需要自己编写内存池了。下面要介绍的内存池使用了散列表。
数据结构
内存池维护着两个表:一个是已申请的内存块列表,另一个是空闲内存块索引表。如下图。
上图中的内存块分成内存块头部和可用部分。内存块头部存放着内存块大小,下一个内存块地址等信息。可用部分在空闲时,会被赋值一个空闲头部,以存放空闲块大小和下一个空闲块地址等信息。如下图。
内存块列表存放所有内存块(包括在用的和空闲的),当最后要删除内存池的时候,只需要释放这个列表就可以了。每次新申请的内存块都会被插入到列表头。当释放内存会池中时,不需要将内存块从该列表中移走。
空闲块列表是一个对空闲内存块的索引列表,编号N存放可用内存大小大于2N且小于2(N+1)的内存块。每当有内存块被释放回池中时,会根据这个内存块的大小被放在对应的索引列表中,插入时和内存块列表相同,放在列表头。
学而时习之
分析完Gray Watson的内存池源码后,我根据自己的理解和回忆,模仿mpool写了一个自己的内存池,虽然大部分和Gray的是一样的,但还是对一些局部的算法做了简化或者修改。比如对空闲队列做排序,以提高空闲块的利用率(牺牲了速度),简化了各个结构体结构等等。有兴趣可以通过http://download.csdn.net/source/2885263下载
后记
这几天继续学习内存管理,才发现之前对内存管理的了解还十分幼稚。这里推荐一篇不错的文章——《内存管理内幕》。