我不确定我是否在问一个新手问题,但我开始了。我也搜索了很多类似的问题,但是一无所获。
因此,我知道mmap
和brk
是如何工作的,并且无论您输入的长度如何,它都会将其四舍五入到最近的页面边界。我也知道malloc
使用brk
/sbrk
或mmap
(至少在Linux/Unix系统上),但这提出了一个问题:malloc
也四舍五入到最接近的页面大小吗?对我来说,页面大小是4096字节,所以如果我想用malloc
分配16字节,4096字节是……比我要求的要多得多。
malloc和friends的基本工作是管理这样一个事实,即操作系统通常只能(有效地)处理大型分配(整个页面和页面区段),而程序通常需要更小的块和更细粒度的管理。
所以malloc(通常)所做的是,它第一次被调用时,它从系统中分配大量的内存(通过mmap或sbrk——可能是一个页面或多个页面),并使用其中的一小部分用于某些数据结构来跟踪堆使用情况(堆在哪里,哪些部分在使用,哪些部分是空闲的),然后将其余的空间标记为空闲空间。然后,它从该空闲空间中分配您请求的内存,并保留剩余的内存供后续的malloc调用使用。
所以当你第一次调用malloc(例如16字节)时,它会使用mmap或sbrk来分配一个大块(可能是4K或64K或16MB甚至更多),并将其初始化为空闲,并返回一个指向16字节的指针。第二次调用malloc获取另外16个字节,只会从该池中返回另外16个字节——不需要返回到操作系统中获取更多。
当你的程序继续分配更多的内存时,它将来自这个池,而free调用将把内存返回给free池。如果它通常分配的内存大于释放的内存,那么这个空闲池最终会耗尽,这时,malloc将调用系统(mmap或sbrk)来获得更多的内存来添加到空闲池中。
这就是为什么如果您使用某种进程监视器监视一个使用malloc/free分配和释放内存的进程,您通常只会看到内存使用上升(当空闲池耗尽并且从系统请求更多内存时),通常不会看到它下降—即使内存被释放,它通常只是回到空闲池,而不是取消映射或返回给系统。有一些例外——特别是涉及到非常大的块的时候——但是一般来说,在进程退出之前,你不能指望任何内存被返回给系统。
#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: %zun", page_size);
printf("difference: %zdn", (ssize_t)(ub - ua));
printf("offsets from start of page: %zu, %zun",
(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
显然起始地址仍然没有对齐;下面的记账必须存储和的指针的指针需要完全满足所需的最大对齐通常——你可以用free
原因——如果free
只是一个指针但需要找出的大小分配,会寻找它,只有两个选择是可行的:在一个单独的数据结构,列出了所有基本指针及其分配大小,或在以下抵消当前指针。其中只有一个是正常的