堆和内存管理是 C 库(可能是 glibc)提供的功能。它会维护堆并在每次执行操作时将内存块返回给您malloc()
。它不知道堆大小限制:每次您请求的内存超过堆上可用的内存时,它都会向内核请求更多内存(使用sbrk()
or mmap()
).
默认情况下,当需要时,内核几乎总是会为您提供更多内存。这意味着malloc()
将始终返回有效地址。只有当您第一次引用分配的页面时,内核才会真正为您查找页面。如果它发现它无法给你一个,它就会运行一个 OOM 杀手,根据某种措施称为badness(其中包括您的进程及其子进程的虚拟内存大小、良好级别、总体运行时间等)选择受害者并向其发送SIGTERM
。这种内存管理技术称为过量使用,由内核在以下情况下使用:/proc/sys/vm/overcommit_memory
是 0 或 1。参见过度使用会计 http://www.mjmwired.net/kernel/Documentation/vm/overcommit-accounting有关详细信息,请参阅内核文档。
通过将 2 写入/proc/sys/vm/overcommit_memory
您可以禁用过量使用。如果你这样做,内核实际上会在承诺之前检查它是否有内存。这将导致malloc()
如果没有更多内存可用,则返回 NULL。
您还可以设置进程可以分配的虚拟内存的限制setrlimit()
and RLIMIT_AS
或与ulimit -v
命令。无论上述过度使用设置如何,如果进程尝试分配超过限制的内存,内核将拒绝它并malloc()
将返回 NULL。请注意现代 Linux 内核(包括整个 2.6.x 系列)对驻留大小的限制(setrlimit()
with RLIMIT_RSS
or ulimit -m
命令)无效。
下面的会话在内核 2.6.32 上运行,具有 4GB RAM 和 8GB 交换空间。
$ cat bigmem.c
#include <stdlib.h>
#include <stdio.h>
int main() {
int i = 0;
for (; i < 13*1024; i++) {
void* p = malloc(1024*1024);
if (p == NULL) {
fprintf(stderr, "malloc() returned NULL on %dth request\n", i);
return 1;
}
}
printf("Allocated it all\n");
return 0;
}
$ cc -o bigmem bigmem.c
$ cat /proc/sys/vm/overcommit_memory
0
$ ./bigmem
Allocated it all
$ sudo bash -c "echo 2 > /proc/sys/vm/overcommit_memory"
$ cat /proc/sys/vm/overcommit_memory
2
$ ./bigmem
malloc() returned NULL on 8519th request
$ sudo bash -c "echo 0 > /proc/sys/vm/overcommit_memory"
$ cat /proc/sys/vm/overcommit_memory
0
$ ./bigmem
Allocated it all
$ ulimit -v $(( 1024*1024 ))
$ ./bigmem
malloc() returned NULL on 1026th request
$
在上面的示例中,交换或 OOM 终止永远不会发生,但如果进程实际上尝试接触所有分配的内存,情况就会发生重大变化。
直接回答你的问题:除非你明确设置了虚拟内存限制ulimit -v
命令,除了机器的物理资源或地址空间的逻辑限制(与 32 位系统相关)之外,没有堆大小限制。你的 glibc 将继续在堆上分配内存,并随着堆的增长向内核请求越来越多的内存。如果所有物理内存都耗尽,最终可能会导致交换效果不佳。一旦交换空间耗尽,内核的 OOM 杀手就会杀死一个随机进程。
但请注意,除了缺少可用内存、碎片或达到配置的限制之外,内存分配失败的原因可能还有很多。这sbrk()
and mmap()
glib 分配器使用的调用有其自身的失败,例如程序中断到达另一个已分配的地址(例如共享内存或先前映射的页面)mmap()
)或已超出进程的最大内存映射数。