早期UNIX的
malloc()
实现使用
sbrk()
/
brk()
系统调用。但是现在,实现使用
mmap()
和
sbrk()
。glibc的
malloc()
实现(这可能是您在Ubuntu 14.04上使用的实现)同时使用
sbrk()
和
mmap()
,当您请求分配时,选择使用哪个通常取决于分配请求的大小,glibc会动态地进行选择。
对于小的分配,glibc使用sbrk()
,而对于较大的分配,它使用mmap()
。宏M_MMAP_THRESHOLD
用于决定这一点。目前,它的默认值为128K。这就解释了为什么您的代码可以分配135152字节,因为它大约是128K。即使您只请求1个字节,您的实现也会分配128K以进行高效的内存分配。因此,在超过此限制之前,不会发生段错误。
您可以使用mallopt()
通过更改默认参数来调整M_MAP_THRESHOLD
。
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阈值的动态调整。
mallopt(M_MMAP_THRESHOLD, 0);
在调用
malloc()
之前,你可能会看到不同的限制。其中大部分是实现细节,C标准规定写入进程不拥有的内存是
未定义行为,因此你需自行承担风险--否则,
恶魔可能从你的鼻子里飞出来;-)
sbrk
是一个系统调用(2),而malloc
是一个库函数(3)。 - Tom Zych