malloc 会四舍五入到最接近的页面大小吗?

Will malloc round up to the nearest page size?

我不确定我是不是在问菜鸟问题,但我开始了。我也搜索了很多类似的问题,但一无所获。

所以,我知道 mmapbrk 是如何工作的,而且无论您输入的长度是多少,它都会将其四舍五入到最近的页面边界。我也知道 malloc 使用 brk/sbrkmmap (至少在 Linux/Unix 系统上)但这提出了一个问题: malloc 也四舍五入到最接近的页面大小?对我来说,页面大小是 4096 字节,所以如果我想用 malloc 分配 16 字节,4096 字节是......比我要求的多很多。

#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <unistd.h>


int main(void) {
    void *a = malloc(1);
    void *b = malloc(1);
    uintptr_t ua = (uintptr_t)a;
    uintptr_t ub = (uintptr_t)b;
    size_t page_size = getpagesize();

    printf("page size: %zu\n", page_size);
    printf("difference: %zd\n", (ssize_t)(ub - ua));
    printf("offsets from start of page: %zu, %zu\n",
        (size_t)ua % page_size, (size_t)ub % page_size);
}

打印

page_size: 4096
difference: 32
offsets from start of page: 672, 704

很明显在这个案例中它没有四舍五入到页面大小,这证明它并不总是四舍五入到页面大小。


如果您将分配更改为某个任意大的大小,它将命中 mmap。例如:

void *a = malloc(10000001);
void *b = malloc(10000003);

我得到:

page size: 4096
difference: -10002432
offsets from start of page: 16, 16

显然起始地址仍然没有页对齐;簿记必须存储在 below 指针 and 指针需要充分对齐以获得通常需要的最大对齐 - 你可以用 free - 如果 free 只是给了一个指针,但它需要计算出分配的大小,在哪里可以找到它,只有两种选择是可行的:在一个单独的数据结构中列出所有基数指针及其分配大小,或者在当前指针下方的某个偏移量处。其中只有一个 sane.

malloc 及其朋友的基本工作是管理这样一个事实,即 OS 通常只能(有效地)处理大型分配(整个页面和页面范围),而程序通常需要较小的块和更细粒度的管理。

那么 malloc(通常)所做的是,第一次调用它时,它会从系统中分配大量内存(通过 mmap 或 sbrk——可能是一页,也可能是很多页),并使用一些数据结构的少量用于跟踪堆使用(堆在哪里,哪些部分正在使用以及哪些部分是空闲的)然后将其余部分 space 标记为空闲。然后它会分配您从该空闲 space 请求的内存,并保留其余内存以供后续 malloc 调用使用。

因此,当您第一次为 16 个字节调用 malloc 时,它将使用 mmap 或 sbrk 分配一个大块(可能是 4K 或 64K 或可能是 16MB 甚至更多)并将其初始化为大部分空闲 return 你指向某处 16 字节的指针。第二次调用 malloc 获取另外 16 个字节只会 return 您从该池中再获取 16 个字节——无需返回 OS 获取更多。

随着您的程序继续 mallocing 更多内存,它将仅来自此池,并且免费调用会将 return 内存分配到空闲池。如果它通常分配的内存多于释放的内存,最终空闲池将 运行 耗尽,此时,malloc 将调用系统(mmap 或 sbrk)以获取更多内存以添加到空闲池中。

这就是为什么如果您使用某种进程监视器监视正在使用 malloc/free 分配和释放内存的进程,您通常只会看到内存使用量上升(因为空闲池 运行s out 并且从系统请求更多内存),并且通常不会看到它下降 - 即使正在释放内存,它通常只是回到空闲池并且不会被取消映射或 return编辑到系统。有一些例外——特别是如果涉及非常大的块——但通常你不能依赖任何内存 returned 到系统,直到进程退出。