为什么动态内存分配在 600MB 后失败

why does dynamic memory allocation fail after 600MB?

本文关键字:600MB 失败 分配 动态 内存 为什么      更新时间:2023-10-16

>我使用三维字符数组实现了一个布隆过滤器(位表(,它运行良好,直到它到达无法再分配内存并给出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位平台(服务器(上。

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

您可以使用 SysInternals 的 VMMap 实用程序在进程的虚拟内存映射中获得更多见解。 您也许能够更改 DLL 的基址,以便它不会位于地址空间的空白区域中。 然而,你获得超过650 MB的几率很低。

64 位操作系统上有更多的喘息空间,32 位进程具有 4 GB 的地址空间,因为操作系统组件在 64 位模式下运行。 您必须使用/LARGEADDRESSAWARE 链接器选项来允许进程使用全部内容。 尽管如此,这仅适用于 64 位操作系统,您的程序仍然可能在 32 位操作系统上轰炸。 当您确实需要这么多 VM 时,最简单的方法是将 64 位操作系统作为先决条件,并构建面向 x64 的程序。

一台 32 位计算机为您提供 4GB 的地址空间。

操作系统保留了其中的一部分(在Windows上默认保留了其中的一半,为您提供2GB的存储空间。我不确定Linux,但我相信它保留了1GB(

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

在这个空间中,需要满足以下几点:

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

以及其他一些细节。

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

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

对于可以分配的内存块的大小没有固定限制。答案是"视情况而定"。这取决于进程内存空间的精确布局。但在 32 位计算机上,您不太可能在单个分配中分配 500MB 或更多。

理论上,

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

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

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