【问题标题】:why does dynamic memory allocation fail after 600MB?为什么动态内存分配在 600MB 之后会失败?
【发布时间】:2012-05-09 02:07:18
【问题描述】:

我使用三维 char 数组实现了一个布隆过滤器(位表),它运行良好,直到它无法再分配内存并给出 bad_alloc 消息。它在分配 600MB 后的下一个扩展请求中给了我这个错误。

布隆过滤器(数组)预计会增长到 8 到 10GB。

这是我用来分配(扩展)位表的代码。

unsigned char ***bit_table_=0;
unsigned int ROWS_old=5;
unsigned int EXPND_SIZE=5;


void expand_bit_table()
     {
         FILE *temp;
         temp=fopen("chunk_temp","w+b");
         //copy old content
         for(int i=0;i<ROWS_old;++i)
             for(int j=0;j<ROWS;++j)
                 fwrite(bit_table_[i][j],COLUMNS,1,temp);
         fclose(temp);
         //delete old table
         chunk_delete_bit_table();
         //create expanded bit table ==> add EXP_SIZE more rows
         bit_table_=new unsigned char**[ROWS_old+EXPND_SIZE];
         for(int i=0;i<ROWS_old+EXPND_SIZE;++i)
            {
                bit_table_[i]=new unsigned char*[ROWS];
                for(int k=0;k<ROWS;++k)
                    bit_table_[i][k]=new unsigned char[COLUMNS];
            }
         //copy back old content

          temp=fopen("chunk_temp","r+b");
         for(int i=0;i<ROWS_old;++i)
         {
            fread(bit_table_[i],COLUMNS*ROWS,1,temp);
         }
          fclose(temp);
         //set remaining content of bit_table_to 0
         for(int i=ROWS_old;i<ROWS_old+EXPND_SIZE;++i)
             for(int j=0;j<ROWS;++j)
                 for(int k=0;k<COLUMNS;++k)
                     bit_table_[i][j][k]=0;

         ROWS_old+=EXPND_SIZE;
     }

数组的最大允许大小是多少,如果这不是问题,我该怎么办。

编辑: 它是使用32位平台开发的。

它运行在具有 8GB RAM 的 64 位平台(服务器)上。

【问题讨论】:

  • 这段代码连眼睛都疼,肯定不行。
  • 640MB 对于每个人来说应该足够了。哎呀,我大声说出来了吗?我的意思是,你有多少内存,这是 64 位环境吗?
  • 你用的是什么系统?你不能指望一个 32 位系统有一个大小为 6GB 的数组 [因为有 4GB 虚拟地址,如果我没记错的话,其中大约 1GB 是为内核保留的,至少在 linux 上]
  • 你们在哭什么?代码并没有那么糟糕,它非常简单且切中要害。并非所有内容都需要分布在六个不同文件中的一百个函数中。这就是生产代码的样子。
  • @MahmoudAl-Qudsi:我见过看起来不像这样的生产代码。但我也认为没有理由不尝试编写更好的代码。说“这就是真实世界代码的样子”只是意味着我们甚至不应该尝试来编写更好的代码。这是荒谬的。我们当然应该。但话虽如此,这显然是 C,而不是 C++。而且随着 C 代码的发展,我认为这并不是特别糟糕。如果我们称它为 C++ 代码,那将是可怕。但除了 C++ 标签,我认为没有理由相信它是 C++。

标签: c++ dynamic-arrays bitarray bad-alloc


【解决方案1】:

32 位程序必须从虚拟内存地址空间分配内存。它存储代码和数据块,内存是从它们之间的孔中分配的。是的,您可以期望的最大值约为 650 兆字节,这是最大的可用漏洞。从那里迅速下降。你可以通过让你的数据结构更智能来解决这个问题,比如树或列表,而不是一个巨大的数组。

您可以使用 SysInternals 的 VMMap 实用程序更深入地了解进程的虚拟内存映射。您也许可以更改 DLL 的基地址,使其不会位于地址空间的其他空白区域的中间。然而,您获得超过 650 MB 的可能性很小。

64 位操作系统有更多的喘息空间,32 位进程有 4 GB 的地址空间,因为操作系统组件以 64 位模式运行。您必须使用 /LARGEADDRESSAWARE 链接器选项以允许进程使用它。尽管如此,这只适用于 64 位操作系统,您的程序仍然可能会在 32 位操作系统上崩溃。当你真的需要这么多虚拟机时,最简单的方法就是将 64 位操作系统作为先决条件,然后构建面向 x64 的程序。

【讨论】:

  • 谢谢。你是说换平台就不会出现这个问题了?
  • 不行,64位地址空间只受分页文件大小的限制。
  • @HansPassant:不正确。在 Unix 机器上,系统管理员可以对单个进程消耗的虚拟内存设置硬限制和软限制。用户可以提高软限制,但不能高于硬限制。但是你的帖子的要点是约翰需要在 64 位机器上编译和运行是正确的。 +1。
  • 这是关于 Unix 的吗?我没有看到任何地方提到过。 OP 使用的是 Windows 术语。
【解决方案2】:

32 位机器为您提供 4GB 的地址空间。

操作系统会保留其中的一部分(Windows 上默认保留一半,给你自己 2GB。我不确定 Linux,但我相信它会保留 1GB)

这意味着您有 2-3 GB 的空间用于您自己的进程。

进入这个空间,有几件事需要适应:

  • 您的可执行文件(以及所有动态链接的库)被内存映射到其中
  • 每个线程都需要一个堆栈

还有很多其他细节。

关键是你最终实际使用了多少内存并不重要。但是很多不同的部分必须适合这个内存空间。而且由于它们没有紧紧地挤在它的一端,它们分割内存空间。想象一下,为简单起见,您的可执行文件被映射到此内存空间的中间。这会将您的 3GB 分成两个 1.5GB 的块。现在假设您加载了两个动态库,它们将这两个块细分为四个 750MB 的块。然后你有几个线程,每个线程都需要更多的内存块,进一步分割剩余的区域。当然,实际上它们中的每一个都不会被放置在每个连续块的确切中心(这将是一个非常愚蠢的分配策略),但是,所有这些内存块都细分了可用内存空间,将其切割分成许多小块。

您可能有 600MB 的可用内存,但很可能没有 600MB 的连续可用内存。因此,在单个 600MB 分配几乎肯定会失败的情况下,六个 100MB 分配可能会成功。

您可以分配多大的内存没有固定限制。答案是“视情况而定”。这取决于您的进程内存空间的精确布局。但在 32 位机器上,您不可能在一次分配中分配 500MB 或更多。

【讨论】:

    【解决方案3】:

    理论上,32 位进程可以访问的最大内存数据为 4GB(实际上它会小一些)。所以你不能一次在内存中拥有 10GB 的数据(即使操作系统支持更多)。此外,即使您动态分配内存,可用的可用存储空间也受到堆栈大小的进一步限制。

    进程可用的实际内存取决于生成可执行文件的编译器设置。

    如果您确实需要这么多,请考虑将(部分)数据保存在文件系统中。

    【讨论】:

      猜你喜欢
      • 2014-05-31
      • 1970-01-01
      • 2017-11-02
      • 1970-01-01
      • 2020-12-12
      • 2014-01-21
      • 1970-01-01
      • 2014-04-12
      • 2012-06-02
      相关资源
      最近更新 更多