为什么动态内存分配在 600MB 后失败
why does dynamic memory allocation fail after 600MB?
>我使用三维字符数组实现了一个布隆过滤器(位表(,它运行良好,直到它到达无法再分配内存并给出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 数据(即使操作系统支持更多数据(。 此外,即使您动态分配内存,可用的可用存储也会受到堆栈大小的进一步限制。
进程可用的实际内存取决于生成可执行文件的编译器设置。
如果您确实需要那么多,请考虑在文件系统中保留(部分(数据。
- 如果没有malloc,链表实现将失败
- 模板参数替换失败,并且未完成隐式转换
- 具有默认模板参数的多态类的模板推导失败
- 视图中的参数推导失败:take_while
- 链接到自行创建的dll失败
- 带有特殊路径部分的"std::filesystem::weakly_canonical"失败
- GetShortPathName在网络驱动器上使用中文文件夹时失败
- gcc和c++17的过载解析失败
- 为什么使用 P/Invoke 调用 dll 时,某些计算机中的 LoadLibrary 失败?
- 在WSL:configure_file上对config_file的每次调用都失败:配置文件时出现问题
- 使用 GCC 卸载的 OpenMP 卸载失败,并出现"Ptx assembly aborted due to errors"
- 使用cmake从源代码构建MySQL连接器/C++失败(与以前的声明冲突)
- 链接阶段在Ubuntu上失败,但在MacOS上失败
- 从父数组测试用例构造二叉树失败
- LibGit2 SSH身份验证失败
- 如何让LLDB在成功时退出,在失败时等待
- VS2017,C++包含目录与附加包含目录,子文件夹包含失败-但为什么
- 生成MRPT库时cmake配置失败
- 为什么除非添加括号,否则构造函数上的模板替换会失败?
- 为什么动态内存分配在 600MB 后失败