关于 malloc 如何根据请求的内存大小选择使用 brk 还是 mmap 的机制,是 glibc(GNU C Library)中 malloc 实现的一个常见策略,尽管具体的阈值(如128KB)可能会因 glibc 的不同版本或配置而有所不同。
brk
brk 是一个系统调用,用于改变数据段的结束地址(即“程序断点”)。在 UNIX 和类 UNIX 系统中,进程的内存布局通常包括代码段、数据段、堆区和栈区。brk 系统调用允许程序动态地增加或减少堆区的大小。
- 增加堆区大小:当程序需要更多的堆内存时,brk 会将程序断点向高地址移动,从而扩展堆区。这是通过修改内核中与该进程相关联的内存映射来实现的。
- 减少堆区大小(在某些系统上可能不支持):虽然理论上 brk 可以用来减少堆区大小,但实际上这种操作很少见,因为一旦内存被分配,即使不再需要,通常也不会立即释放给操作系统,而是由 malloc 内部管理,以便重用。
- 图解(小林):
mmap
mmap 是一种更强大的内存映射机制,它允许进程将文件或其他对象映射到进程的地址空间中。然而,mmap 也可以用于匿名映射,即不映射到任何文件,而是直接在进程的地址空间中分配一块内存。
- 匿名映射:当 mmap 用于匿名映射时,它会在进程的地址空间中创建一个新的内存区域,这个区域对进程是私有的,不会被其他进程看到。这块内存可以被用作堆内存的一部分,或者用于其他目的,如共享内存(通过不同的映射选项)。
- 优点:mmap 提供了更好的内存管理灵活性,因为它可以分配任意大小的内存块,并且这些内存块在物理内存中的位置可以是离散的。此外,mmap 分配的内存可以更容易地与其他进程共享(如果使用适当的映射选项)。
- 缺点:与 brk 相比,mmap 可能会产生更多的系统调用开销,因为每次分配都需要通过系统调用来完成。然而,这种开销通常被 mmap 的灵活性和功能所抵消。
- 图解(小林):
malloc 的实现
在 glibc 的 malloc 实现中,当请求的内存大小小于某个阈值时(如128KB),malloc 会倾向于使用 brk 来扩展堆区,因为这种方式通常更快且系统调用开销更低。然而,当请求的内存大小超过这个阈值时,malloc 会选择使用 mmap 来分配内存,因为这样可以避免在堆区产生大量的内存碎片,并且 mmap 分配的内存块可以更容易地独立管理。
需要注意的是,虽然 malloc、brk 和 mmap 是内存管理的关键组件,但它们的具体实现和行为可能会因不同的操作系统、库版本和配置而有所不同。