malloc
实现使用brk
/sbrk
作为从操作系统申请内存的主要手段。然而,它们还使用mmap
来获取大块内存分配。使用brk
而不是mmap
是否真的有好处,还是只是传统惯例?全部使用mmap
是否同样有效呢?(注意:我在这里交替使用
sbrk
和brk
,因为它们是相同的Linux系统调用接口brk
。)
作为参考,这里有几篇描述glibc malloc
的文档:
GNU C Library参考手册: GNU分配器
https://www.gnu.org/software/libc/manual/html_node/The-GNU-Allocator.html
glibc维基: Malloc概述
https://sourceware.org/glibc/wiki/MallocInternals
这些文档描述了sbrk
用于声明小型分配的主要竞技场,mmap
用于声明次要竞技场,并且mmap
也用于为大型对象(“远大于一页”)申请空间。
使用应用程序堆(使用sbrk
声明)和mmap
引入了一些额外的复杂性,这可能是不必要的:
[Glibc malloc是从已分配的竞技场 - 主竞技场使用应用程序的堆。其他竞技场使用
mmap
的堆。要将块映射到堆,需要知道哪种情况适用。如果该位为0,则该块来自主竞技场和主堆。如果该位为1,则该块来自mmap
的内存,并且可以从块的地址计算出堆的位置。
ptmalloc
派生而来,而ptmalloc
则是从1987年开始的dlmalloc派生而来的。]
jemalloc手册(http://jemalloc.net/jemalloc.3.html)中提到:
传统上,分配器使用
sbrk(2)
获取内存,这种方式存在多种缺点,包括竞争条件、增加的碎片和对最大可用内存的人为限制。如果操作系统支持sbrk(2)
,则该分配器将按照mmap(2)
和sbrk(2)的顺序使用两者;否则,只使用mmap(2)
。所以,即使他们已经费尽心思编写代码使其可以不使用
sbrk
,但他们仍然使用它,甚至在此处明确表示sbrk
是次优的。[编写jemalloc始于2005年。]
更新:进一步思考,“按照优先顺序”这句话让我有了一个探究的方向。为什么要按照这个顺序?他们只是将
sbrk
作为备选项,以防mmap
不被支持(或缺少必要的功能),还是可能出现某种状态使得进程可以使用sbrk
但无法使用mmap
?我将查看他们的代码,看看能否弄清楚它在做什么。
我之所以询问是因为我正在C语言中实现垃圾回收系统,到目前为止我没有发现使用
mmap
之外的任何理由。不过我想知道是否有我忽略的东西。(在我的情况下,我有另一个避免使用
brk
的原因,那就是我可能需要在某个时候使用malloc
。)
mmap
来为成千上万个较小的分配分配一个池,对吧?而不是像为大型分配那样每个分配都使用一个mmap
。 - that other guymmap()
的malloc()
版本。 - Barmarmmap
。jemalloc
关于brk
的负面评论最适用于将其用于所有内容,例如古老的Unix历史malloc实现。 (特别是碎片化:如果短期大量分配后存在长期小额分配,则无法将内存返还给内核。) - Peter Cordes