堆大小的合适范围是多少?

3
我的程序是用C语言编写的,使用gcc进行编译。我正在读取一个文件,并将文件内容存储到缓冲区中。为了做到这一点,我需要让缓冲区的大小与文件一样大。我使用malloc()为缓冲区分配内存。不幸的是,我遇到了一个277MB的文件。堆内存是否太小?运行时我得到了一个段错误,但没有更多的信息。它曾经处理过160 MB大的文件,但是这个单独的异常277MB的文件却导致了错误。
编辑:valgrind给了我@0xC0000022L。
==6380== Warning: set address range perms: large range [0x8851028, 0x190e6102) (undefined)
==6380== Warning: set address range perms: large range [0x8851028, 0x190e6028) (defined)
==6380== Warning: set address range perms: large range [0x190e7028, 0x2997c108) (undefined)
==6380== Warning: set address range perms: large range [0x190e7028, 0x2997c028) (defined)
==6380== Warning: silly arg (-1737565464) to malloc()
==6380== Invalid write of size 4
==6380==    at 0x8048A49: main (newanalyze.c:85)
==6380==  Address 0x4a00 is not stack'd, malloc'd or (recently) free'd
==6380==
==6380==
==6380== Process terminating with default action of signal 11 (SIGSEGV)
==6380==  Access not within mapped region at address 0x4A00
==6380==    at 0x8048A49: main (newanalyze.c:85)

但是在第85行只有一个小变量,不应该受到文件大小的影响。


2
检查 malloc 的返回值。一个正确的程序可能会耗尽内存,但它永远不会出现段错误。 - Kerrek SB
2
为什么不在这种情况下使用mmap?此外,为了获得快速到位的分析结果,请在Valgrind下运行程序(调试版本)。 - 0xC0000022L
@0xC0000022L libcдёӯзҡ„mallocдјҡе°ҶеӨ§еқ—еҶ…еӯҳиҜ·жұӮиҪ¬жҚўдёәеҶ…йғЁзҡ„mmapи°ғз”ЁгҖӮ - tdenniston
是的,这取决于实现方式。但我的观点是,“mmap”已经提供了获取文件内容“指针”的方式。那么为什么还要绕个弯呢? - 0xC0000022L
@0xC0000022L 抱歉,我阅读问题过快,错过了他正在将文件映射到内存的事实。你是正确的。 - tdenniston
显示剩余6条评论
3个回答

4
请注意Valgrind的输出:
==6380== 警告:silly arg (-1737565464) to malloc()
-1737565464是一个有符号整数,如果将其视为无符号整数,则它是2557401832(> 2G)。您正在向malloc传递一个> 2G的参数,而不是277M。
从以下信息中,我们知道您正在尝试写入地址0x4a00,这是一个无效地址,在这种情况下,您会遇到SEGV。请检查您代码中的newanalyze.c:85以查看其中是什么。
==6380== Invalid write of size 4 ==6380== at 0x8048A49: main (newanalyze.c:85) ==6380== Address 0x4a00 is not stack'd, malloc'd or (recently) free'd

1

回复其中一条评论,这里是使用mmap(2)打开文件的方法。假设您正在使用Unix系统。

int fd;
struct stat S;
const char* file_base;

if ((fd = open(filename, O_RDONLY)) < 0) {
    fprintf(stderr, "Can't open file %s to read\n", filename);
    return NULL;
}
if (fstat(fd, &S) != 0) {
    fprintf(stderr, "Can't stat file %s!\n", filename);
    close(fd);
    return NULL;
}
if ((file_base = mmap(NULL, S.st_size, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
    fprintf(stderr, "Unable to map file %s\n", filename);
    close(fd);
    return NULL;
}

完成后,file_base指向一块内存,其中包含了整个文件的内容。

这种方式的优点有:

  • 它几乎不占用任何内存。实际上所发生的是,您将文件作为内存的交换空间。
  • 它不需要额外的时间。新标识的内存位于交换区之外,因此当访问该内存时,所有需要发生的就是交换文件中的那一部分,而这会像使用malloc(3)动态分配内存一样快速。

这将使得内存的那一小块被标记为只读;您也可以用可读写的文件来做这个技巧,但这意味着如果您写入内存,您将同时更改文件。

如果您不在unix上,仍然可能有一个mmap函数可用。 如果没有,那么就有一些windows本地的方法来完成同样的事情(CreateFileMapping)。


对内存映射文件的精彩而简洁的解释。让我省去了写答案的时间,因为我刚刚开始写。我在你的答案中添加了一个 man 页面的链接。+1 - 0xC0000022L
这个答案教会了我一些新东西,但最终并不是我需要做的事情。如果可以的话,我会给它一个+1,但我需要更多的声望。 - jbf81tb

0

很遗憾我无法给出一个确切的“为什么”,但是mmap2似乎是malloc在您的系统上调用的函数,它只是报告内存不足。在这种情况下,malloc将返回NULL并导致段错误。

munmap(0xb7706000, 4096)                = 0
mmap2(NULL, 2557403136, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1  ENOMEM (Cannot allocate memory)

作为一个反例,我有一个玩具程序可以成功地运行:
mmap(NULL, 283652096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f2d00994000

我会检查系统上可用的内存或程序正在使用的内存。也许它正在严重泄漏内存?

问题在于程序无法分配足够的内存来开始执行操作,因此它实际上无法泄漏。但是当尝试为非常大的结构体分配空间时,我确实会从malloc中返回一个空指针,而不是缓冲区。奇怪的是,我认为该结构体应该是缓冲区大小的36倍,但这意味着它应该在160MB文件上失败,因为我只有4GB的RAM。 - jbf81tb
1
实际上,这将取决于系统管理员和调用命令的shell所施加的资源限制(ulimit -a)。此外,考虑到您的地址已经远远超出了32位地址范围,而OP问题中的地址都在32位范围内,这是一个重要的区别。 - 0xC0000022L
实际上,mmap2函数中的内存量是有道理的,因为经过一些重新计算后,该结构体的大小略小于缓冲区的10倍。但是,我的系统信息显示我有5.8 GiB的内存,我猜我错了,应该是有6GB的RAM。所以我不知道为什么分配2.5GB的内存就可以了。 - jbf81tb
core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited file size (blocks, -f) unlimited pending signals (-i) 47541 max locked memory (kbytes, -l) 64 max memory size (kbytes, -m) unlimited open files (-n) 1024 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) 47541 virtual memory (kbytes, -v) unlimited - jbf81tb
这个答案告诉了我问题出在哪里,也教会了我如何使用strace。最终发现是由于一个超大的结构体无法进行太多更改而导致的问题。幸运的是,我只是错误地使两个数组比实际需要的要大,所以我将它们改小后,结构体的大小减少了不到一半。 - jbf81tb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接