分类: 系统运维
2012-03-29 12:56:15
ISO C为内存分配规定了三个函数:
1、malloc,它开辟指定字节数量的内存。内存的初始值是不确定的。
2、calloc、为指定数量的指定尺寸的对象开辟空间。这个空间被初始化为0。
3、realloc、增加或减少之前开辟的区域。当尺寸增加时,它可能会导致把之前开辟的空间移到其它地方,来在尾部提供额外的空间。还有,当尺寸增加时,在旧对空和新区域尾部之间的空间的初始值是不确定的。
这三个分配内存的函数的返回值被保证为适当对齐的,以便它能用于任何数据对象。比如,如果在一个特定系统上的最限制的对齐请求要求double必须从8的倍数的内存地址开始,那么这三个函数返回的所有指针都会如此对齐。
因为这三个alloc函数返回一个通用的void *指针,所以如果我们#include
函数free导致ptr指向的空间被释放。这被释放的空间通常放入可用内存的池时,在下次这三个alloc函数的调用时可以被再次分配。
realloc函数让我们增加或减少前一个被分配区域的尺寸。(最普遍的使用是增加一个区域。)例如,如果我们在我们运行时填满的一个数组里为512个元
素开辟空间,但是发现我们需要多于512个元素的空间,那么我们可调用realloc。如果已有区域末尾之后有可以符合请求的空间,那么realloc不
必移动任何东西,它只简单地在末尾开辟额外的空间然后返回我们传递给它的同样的指针。但是如果在已有区域尾部没有足够的空间,realloc会开辟另一块
足够大的空间,并把已有的512个元素的数组拷贝到这块新区域,并释放旧的数据,然后返回新区域的指针。因为区域可能会移动,所以我们不应该拥有这个区域
的任何指针。第4章的练习展示了realloc和getcwd一起使用来处理任何长度的路径名。17章展示了一个使用realloc来避免一个固定的编译
器尺寸的数组的例子。
注意realloc的最后一个参数是区域的新尺寸,而不是旧尺寸和新尺寸的差别。作为一个特殊的例子,如果ptr是一个空指针,realloc和malloc行为相似,开辟一个指定尺寸的区域。
这些函数的早期版本允许我们来realloc一个我们曾用malloc、realloc或calloc开辟过的但已经释放了的块。这个把戏在版本7上就
有,使用malloc的查找策略来执行存储压缩。Solaris仍然支持这个特性,但许多其它平台已经不支持了。这个特性不赞成被使用,而且也不应该被使
用。
内存分配函数通常用sbrk系统调用实现。这个系统调用扩展(或缩小)进程的堆。malloc和free的一个样本实现在Kernighan and Ritchie[1988]的8.7节给出。
尽管sbrk可以扩展和缩小进程的内存,malloc和free的多数版本都不减少它们的内存尺寸。我们释放的空间可以用作下次的分配,但释放的空间不会返回给内核,而是被malloc池保存。
多数实现开辟比所请求的稍多一点的空间并使用额外的空间来保存记录--分配了的块的尺寸、指向下一个分配了的块的指针等等。意识到这点是重要的。这意味着
在一个分配了的区域末写可能会覆写之更后面的块的记录保存信息。这些错误类型经常是惨重的,却很难发现,因为这个错误可能会等到很晚才会出现。还有,在分
配的区域开头之前写数据同样可能会覆写记录保存信息。
在一个动态分配的缓存的末尾后面或开头之前写可以破坏比内部记录保存信息更多。在一个动态分配的缓存之前或之后的内存可能潜在地用作其它的动态分配对象。这些对象可以和破坏它们的代码没有关系,这会导致更难找到破坏的源头。
其它可能的致命的错误是释放一个已经被释放的块,还有释放一个不是通过那三个alloc函数得到的一个指针。如果一个进程调用malloc,但忘记了调用
free,它的内存使用会持续增长。这被称为泄露。不调用free来返回不使用的空间的话,进程地址空间的尺寸会缓慢增长,直到没有空闲内存剩余。在这段
时间,性能会因为过量的换页开销而降低。
因为内存分配错误很难被追踪,所以一些系统在这些函数的版本上在每次调用这三个alloc函数或free函数时提供额外的检查。函数的这些版本经常通过为链接器引入一个特殊的库来指定。同样还有公开可用的源码,你可以用特殊的标志编译它们以启用额外的运行期检查。
FreeBSD,Mac OS X和Linux通过环境变量的设置来支持额外的调用。此外,选项可以通过符号链接/etc/malloc.conf传递给FreeBSD库。
替代的内存分配器
malloc和free有很多可用的替代品。一些系统已经包含了提供替代的内存分配实现的库。其它系统只提供了标准分配器,让软件开发员在需要时下载替代品。我们在这里讨论一些替代品。
libmalloc
基于SVR-4的系统,比如Solaris,包含了libmalloc库。它提供了一堆匹配ISO
C内存分配函数的接口。libmalloc库包括mallopt,一个允许进程设置特定的控制存储分配器的变量的函数。一个称为mallinfo的函数同
样可以用来提供内存分配器的统计资料。
vmalloc
Vo[1996]描述了一个允许进程未不同内存区域使用不同的技术的来分配内存的内存分配器。除了vmalloc相关的函数,这个库还提供了ISO C内存分配函数的模拟。
quick-fit
历史上,标准的malloc算法使用最佳适配(best-fit)或最先适配(first-fit)的内存分配策略。快速适配(Quick-fit)比它
们都快,但会使用更多内存。Weinstock and
Wulf[1988]描述了这个算法,它基于把内存分割为不同尺寸的缓冲,并根据缓冲的尺寸在不同的释放列表上维护未使用的缓冲。基于快速适配的
malloc和free的免费实现在几个FTP站点上可读。
alloca函数
还有一个函数值提及。函数alloca有和malloc一样的调用顺序,然而,它不在堆上分配内存,而是在当前函数的栈框架上。好处是我们不必释放内存,
它在函数返回时会自动释放。alloca函数增加了栈框架的尺寸。缺点是一些系统如果在函数调用后无法增加栈框架的尺寸,则不支持alloca。尽管如
此,许多软件包使用它,而实现存在于各个系统上。
本文讨论的四个平台都支持alloca函数。