【问题标题】:allocating 16GB in one single array, malloc fails silently在一个数组中分配 16GB,malloc 静默失败
【发布时间】:2014-10-19 14:01:14
【问题描述】:

我正在处理非常大的数据集。我正在尝试在一个数组中分配 16GB

出于某种我不知道的原因,如果我尝试访问位置(比如说)“6 亿”,我会发现该位置不可访问,并且在运行时出现分段错误错误。

有人知道为什么会这样吗?

我的架构是 64 位的,因此应该可以处理 160 亿个地址,或者至少我是这么认为的。

我的电话是:

int* array = (int*) malloc(sizeof(int)* 1000000000 * 4);

谢谢大家!

@ScottChamberlain,@Sanhdrir:它静默失败,因为它没有返回 NULL 指针。您可能已经注意到,这个数组代表一个矩阵。在以这种方式分配它之前,我尝试使用指向指针的指针来分配它。这需要更多的内存空间(多 80 亿字节)以保存每个指针的地址。通过这种方式,我的程序被杀死了,而现在我没有,但是当我尝试访问某些地址时,我得到了分段错误。

edit 如果我分配 10 个 1.6 亿块(甚至更多)的块,我不会收到任何错误,并且内存已分配。问题在于分配一个大块。我现在的问题变成了:有没有办法克服这个限制?

edit2 @Sanhadrin 你的假设都是正确的,除了我使用 gcc 的事实。 我在这里报告 /proc/meminfo/ 文件的内容 MemTotal: 198049828 kB MemFree: 113419800 kB Buffers: 153064 kB Cached: 5689680 kB SwapCached: 124780 kB Active: 73880720 kB Inactive: 8998084 kB Active(anon): 70843644 kB Inactive(anon): 6192548 kB Active(file): 3037076 kB Inactive(file): 2805536 kB Unevictable: 0 kB Mlocked: 0 kB SwapTotal: 201273340 kB SwapFree: 164734524 kB Dirty: 0 kB Writeback: 0 kB AnonPages: 76915376 kB Mapped: 16376 kB Shmem: 72 kB Slab: 190352 kB SReclaimable: 124660 kB SUnreclaim: 65692 kB KernelStack: 3432 kB PageTables: 259828 kB NFS_Unstable: 0 kB Bounce: 0 kB WritebackTmp: 0 kB CommitLimit: 300298252 kB Committed_AS: 160461824 kB VmallocTotal: 34359738367 kB VmallocUsed: 733424 kB VmallocChunk: 34258351392 kB HardwareCorrupted: 0 kB AnonHugePages: 0 kB HugePages_Total: 0 HugePages_Free: 0 HugePages_Rsvd: 0 HugePages_Surp: 0 Hugepagesize: 2048 kB DirectMap4k: 195520 kB DirectMap2M: 37507072 kB DirectMap1G: 163577856 kB

【问题讨论】:

  • 哪种编程语言 c/c++ 或其他?
  • 堆栈大小(ulimit -s)是多少?
  • 但是您的应用程序本身是 64 位的吗?根据平台、编译器和选项,您可以创建 32 位可执行文件。除此之外,你真的有 16GB 的免费可寻址 RAM 吗?您说“malloc() 静默失败”。你真的是在检查 malloc 的结果是否为 NULL,并检查 errno 以查看失败的原因吗?还是您预计它会崩溃?
  • @Sanhadrin 我有 16GB 或内存,如果我没有,我认为我的程序会被杀死。 malloc的结果不是NULL,我检查一下,如何检查errno? “分段错误”是我尝试访问一些应该可以访问的地址时遇到的唯一错误
  • @Sergio 最后,您不太可能需要在 RAM 中分配 16GB 内存 - 几乎可以肯定有更好的策略。我猜你这样做是为了“性能”(因为你说你使用 C 调用而不是 C++ 是出于同样的原因..)但考虑到你正在面临重大的分页问题,​​除非你是使用专门针对这种用途进行调整的高性能计算机,当然有更好的方法,即在任何时候只将一部分数据保存在内存中。

标签: arrays memory-management malloc


【解决方案1】:

信息量很大,很难肯定回答这个问题,但似乎:

  • 您使用的是 x86-64 架构
  • 您可能正在运行 Linux
  • 您至少有 16GB 的 RAM,但不确定您是否有超过 16GB 的空闲 RAM
  • 您正在使用 gcc 进行编译,其设置配置为创建 64 位二进制文​​件
  • 您对 malloc() 的调用返回了一个看似有效的(非空)指针
  • 对该内存进行索引可能会导致分段错误

如果你阅读 malloc 的手册页,它会说:

注意事项

默认情况下,Linux 遵循乐观的内存分配策略。这意味着当 malloc() 返回非 NULL 时,不能保证内存确实可用。如果发现系统内存不足,OOM 杀手将杀死一个或多个进程。更多信息请参见proc(5)中/proc/sys/vm/overcommit_memory和/proc/sys/vm/oom_adj的描述,以及Linux内核源文件Documentation/vm/overcommit-accounting。

通过阅读 man proc 进行跟进,它指出:

/proc/sys/vm/overcommit_memory 该文件包含内核虚拟内存记帐模式。 值为:

                 0: heuristic overcommit (this is the default)
                 1: always overcommit, never check
                 2: always check, never overcommit

          In mode 0, calls of mmap(2) with MAP_NORESERVE are not
          checked, and the default check is very weak, leading to the
          risk of getting a process "OOM-killed".  Under Linux 2.4, any
          nonzero value implies mode 1.

因此,根据 overcommit_memory 的设置,即使请求的空间不可用,malloc() 也可能返回一个有效指针,因为当您使用那么多内存时,其他进程将被终止,释放增加所需的空间。此处并非如此,因为您是立即使用它——这意味着您实际上并没有 16GB 的可用空间可供使用。进一步:

         In mode 2 (available since Linux 2.6), the total virtual
          address space that can be allocated (CommitLimit in
          /proc/meminfo) is calculated as
              CommitLimit = (total_RAM - total_huge_TLB) *
                            overcommit_ratio / 100 + total_swap

          where:

               *  total_RAM is the total amount of RAM on the system;

               *  total_huge_TLB is the amount of memory set aside for
                  huge pages;

               *  overcommit_ratio is the value in
                  /proc/sys/vm/overcommit_ratio; and

               *  total_swap is the amount of swap space.

          For example, on a system with 16GB of physical RAM, 16GB of
          swap, no space dedicated to huge pages, and an
          overcommit_ratio of 50, this formula yields a CommitLimit of
          24GB.

          Since Linux 3.14, if the value in
          /proc/sys/vm/overcommit_kbytes is nonzero, then CommitLimit is
          instead calculated as:

              CommitLimit = overcommit_kbytes + total_swap

因此,至少,您可以更好地防止它过度使用,因此 malloc() 会按预期失败 - 但根本问题是您要求的空间非常大貌似没有。您可以检查 /proc/meminfo 以查看任何时候实际可用的内存量以及其他内存统计信息,以了解问题所在以及您的实际限制是什么。

【讨论】:

    【解决方案2】:

    如果您需要分配非常大的内存块,您应该使用您的操作系统服务将虚拟内存映射到进程;不是 malloc()。

    如果您希望在系统上分配 16GB,则需要这样做。

    【讨论】:

      猜你喜欢
      • 2016-03-19
      • 2020-11-26
      • 2022-11-10
      • 1970-01-01
      • 2011-04-09
      • 2017-01-10
      • 2018-09-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多