UNIX 的旧malloc()
实现使用sbrk()
/brk()
系统调用。但是现在,实现使用mmap()
and sbrk()
. glibc的malloc()
实现(这可能是您在 Ubuntu 14.04 上使用的那个)同时使用sbrk()
andmmap()
以及在您请求时选择使用哪一个来分配通常取决于分配请求的大小,而 glibc 会动态执行。
对于小的分配,glibc 使用sbrk()
,对于较大的分配,它使用mmap()
. 宏M_MMAP_THRESHOLD
用于决定这一点。目前,它的默认值设置为 128K。这解释了为什么您的代码设法分配了 135152 个字节,因为它大约为 ~128K。尽管您只请求了 1 个字节,但您的实现分配了 128K 以实现高效的内存分配。因此,在您超过此限制之前,不会发生段错误。
您可以M_MAP_THRESHOLD
通过mallopt()
更改默认参数来使用。
M_MMAP_THRESHOLD
对于大于或等于 M_MMAP_THRESHOLD 指定的限制(以字节为单位)且无法从空闲列表中满足的分配,内存分配函数使用 mmap(2) 而不是使用 sbrk(2) 增加程序中断。
使用 mmap(2) 分配内存具有显着优势,即分配的内存块始终可以独立地释放回系统。(相比之下,只有在顶端释放内存时才能修剪堆。)另一方面,使用 mmap(2) 也有一些缺点:释放的空间不会放在空闲列表中以供重用后期分配;内存可能会被浪费,因为 mmap(2) 分配必须是页面对齐的;并且内核必须执行将通过 mmap(2) 分配的内存清零的昂贵任务。平衡这些因素会导致 M_MMAP_THRESHOLD 参数的默认设置为 128*1024。
此参数的下限为 0。上限为 DEFAULT_MMAP_THRESHOLD_MAX:32 位系统上为 512*1024 或 64 位系统上为 4*1024*1024*sizeof(long)。
注意:现在,glibc 默认使用动态 mmap 阈值。阈值初始值为128*1024,但是当大于当前阈值且小于等于DEFAULT_MMAP_THRESHOLD_MAX的块被释放时,阈值向上调整为被释放块的大小。当动态 mmap 阈值生效时,修剪堆的阈值也会动态调整为动态 mmap 阈值的两倍。如果设置了任何 M_TRIM_THRESHOLD、M_TOP_PAD、M_MMAP_THRESHOLD 或 M_MMAP_MAX 参数,则禁用 mmap 阈值的动态调整。
例如,如果您这样做:
#include<malloc.h>
mallopt(M_MMAP_THRESHOLD, 0);
在调用之前malloc()
,您可能会看到不同的限制。其中大部分是实现细节,C 标准表示写入进程不拥有的内存是未定义的行为。因此,请自担风险——否则,恶魔可能会从你的鼻子里飞出;-)