【问题标题】:How big is too big for the heap堆多大才算太大
【发布时间】:2012-07-12 07:39:58
【问题描述】:

我的程序是用 C 语言编写的,我正在使用 gcc 进行编译。我正在读取文件,并将文件的内容存储到缓冲区中。为此,我需要缓冲区与文件一样大。我正在使用 malloc() 为缓冲区分配内存。不幸的是,我遇到了一个 277MB 的文件。这对堆来说太多了吗?我在运行时遇到了段错误,但没有更多信息。它适用于大至 160 MB 的文件,但这个 277MB 文件的单个异常值正在破坏它。

编辑:valgrind 给我

@0xC0000022L valgrind 给我

==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 行只是一个不受文件大小影响的小变量。

【问题讨论】:

  • 有用的是“strace ”和操作系统信息。对于现代堆来说,大小并不太大,而且我很确定 Linux 上的 malloc 与股票 GCC 很容易分配空间。
  • 查看malloc的返回值。一个正确的程序可能会耗尽内存,但它永远不会出现段错误。
  • 为什么不在这种情况下使用mmap?此外,要获得快速准确的分析,请在 Valgrind 下运行程序(调试版本)
  • mediafire.com/?y667x54elblca0a 这是 strace 信息。这是来自 uname Linux ubuntu 3.2.0-26-generic-pae #41-Ubuntu SMP Thu Jun 14 16:45:14 UTC 2012 i686 i686 i386 GNU/Linux 的操作系统信息
  • 我认为如果你可以部分分配整个文件在内存中是不好的

标签: c memory heap-memory


【解决方案1】:

不幸的是,我不能给你一个可靠的“为什么”,但是 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 周期。
  • 实际上,这取决于系统管理员施加的资源限制以及调用命令的 shell (ulimit -a)。此外,鉴于您的地址已经很好地进入了 32 位以外的地址范围,而 OP 问题上的地址都在 32 位范围内,所以您有一个重要的区别。
  • 实际上,作为 mmap2 函数中的内存量,这是有道理的,因为经过重新计算后,结构体的大小略小于缓冲区大小的 10 倍。但是,我的系统信息仍然显示我有 5.8 GiB 的内存,我想我弄错了并且有 6GB 的 RAM。所以我不知道为什么2.5GB的内存分配得到它。
  • 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
  • 这个答案告诉我有多大的事情搞砸了一切,并教会了我关于 strace 的知识。它最终成为无法改变太多的超大型结构的麻烦。幸运的是,我刚刚错误地使两个数组比我实际需要的大,所以改变了它们,它把结构的大小减少了不到一半。
【解决方案2】:

跟进其中一个 cmets,以下是使用 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 指向包含文件全部内容的内存位。

这种方式的优点是:

  • 它基本上不占用内存(有点)。这里实际发生的情况是,您将文件用作交换空间以获得一点内存。
  • 不需要时间(有点)。新识别的内存位开始换出,因此当访问内存时,所要做的就是换入该位文件,这与使用 @ 动态分配时一样快987654322@.

这块内存被标记为只读;您也可以对文件进行读写操作,但这意味着如果您写入内存,您将同时更改文件。

如果您不在 unix 上,您可能仍然可以使用 mmap 函数。如果没有,会有一些 Windows 原生的方式来做同样的事情 (CreateFileMapping)。

【讨论】:

  • 对内存映射文件的出色而简洁的解释。省得我写下我刚刚开始的答案。在您的答案中添加了指向手册页的链接。 +1
  • 这个答案教会了我一些新的东西,但它最终不是我需要做的事情。如果可以的话,我会给它+1,但我需要更多的声誉。
【解决方案3】:

请注意 Valgrind 的输出,

==6380== 警告:愚蠢的 arg (-1737565464) 到 malloc()

-1737565464 是有符号的 int 值,如果作为无符号的 int 则为 2557401832 (>2G)。您将 >2G 参数传递给 malloc 而不是 277M。

根据以下信息,我们知道您正在尝试写入地址 0x4a00,这是一个无效地址,在这种情况下您会期望 SEGV。请检查您的代码中的 newanalyze.c:85 以查看其中的内容。

==6380== 大小为 4 的无效写入

==6380== at 0x8048A49: main (newanalyze.c:85)

==6380== 地址 0x4a00 没有被堆栈、malloc 或(最近)释放

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-11-26
    • 1970-01-01
    • 2011-02-28
    • 2011-03-28
    • 2012-09-23
    • 2014-10-01
    • 2011-05-25
    • 1970-01-01
    相关资源
    最近更新 更多