mmap()分配大内存失败求助:虚拟内存充足仍报errno12
问题分析与解决方案
你遇到的问题核心在于错误地将RLIM_INFINITY(无限制的虚拟内存上限)当作实际可分配的虚拟地址空间大小来计算,导致你尝试分配的内存大小远超系统硬件支持的实际虚拟地址空间,最终触发ENOMEM(错误码12)。
为什么会出现这个问题?
看你的输出:memory size is 18446744073709551615 bytes,这个值是RLIM_INFINITY——它表示系统允许进程使用的虚拟内存没有硬限制,但这不代表你的进程真的能分配这么大的内存:
- 64位架构(比如x86_64)的用户态虚拟地址空间是有硬件上限的,通常只有128TB(对应247位地址),而你计算的`array_size`是2.3EB(261字节),这远远超出了实际可用的虚拟地址范围,
mmap根本找不到这么大的连续虚拟地址区间来分配。 - 即使忽略地址空间限制,这么大的内存也需要对应的交换空间来支撑写入操作,几乎没有系统会配置这么大的交换分区。
你尝试的方法为什么无效?
MAP_NORESERVE:这个标志只是告诉内核不要预分配交换空间,但解决不了虚拟地址空间不足的问题——内核还是需要找到足够大的连续虚拟地址范围,而你的请求大小已经远超硬件支持的上限。- 减小到1/32:即使是1/32,
RLIM_INFINITY/32也有5.7e17字节,还是远大于128TB,所以依然会失败。
解决方案
- 先判断虚拟内存上限是否为无限大:如果
mem_limit.rlim_cur等于RLIM_INFINITY,就用对应架构的实际虚拟地址空间上限来计算,而不是直接用这个无限大的值。 - 基于系统架构设置合理的上限:比如x86_64用户态虚拟地址空间最大为128TB(
1ULL << 47),你可以用这个值来计算分配大小。 - 动态检测可用虚拟地址空间:或者通过尝试逐步分配的方式,找到最大的可分配连续内存。
修改后的代码示例
#include <stdio.h> #include <stdlib.h> #include <sys/resource.h> #include <sys/mman.h> #include <errno.h> #include <stdint.h> int main() { struct rlimit mem_limit; if (getrlimit(RLIMIT_AS, &mem_limit) != 0) { return -errno; } // 处理RLIM_INFINITY的情况,用x86_64的用户态虚拟地址空间上限(128TB) uint64_t max_virtual_mem; if (mem_limit.rlim_cur == RLIM_INFINITY) { // x86_64用户态虚拟地址空间通常是128TB(2^47字节) max_virtual_mem = 1ULL << 47; } else { max_virtual_mem = mem_limit.rlim_cur; } // 设置虚拟内存上限为最大允许值 mem_limit.rlim_cur = mem_limit.rlim_max; if (setrlimit(RLIMIT_AS, &mem_limit) != 0) { return -errno; } uint64_t array_size = max_virtual_mem / 8; printf("max virtual memory is %lu bytes, array size is %lu bytes\n", max_virtual_mem, array_size); char* mem_array = (char*)mmap(0, array_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (mem_array == MAP_FAILED) { printf("mmap failed with %d. allocation size = %lu\n", errno, array_size); return -errno; } printf("mmap succeeded, address: %p\n", mem_array); // 尝试写入一个字节测试 mem_array[0] = 'a'; printf("written first byte successfully\n"); munmap(mem_array, array_size); return 0; }
额外注意事项
- 不同架构的虚拟地址空间上限不同:比如ARM64的用户态虚拟地址空间可能是48位(256TB),你可以根据目标架构调整
max_virtual_mem的值。 - 即使
mmap成功,写入大量数据时依然需要足够的物理内存或交换空间,否则会触发OOM killer。 - 如果不需要连续内存,可以多次调用
mmap分配小块内存,而不是追求一个超大的连续块。
内容的提问来源于stack exchange,提问作者Bar Raveh
相关产品推荐
相关产品推荐

