最大 malloc 大小低于预期

Irb*_*bis 2 c++ memory-management

这是一个检查最大 malloc 大小的简单程序:

#include <iostream>

std::size_t maxDataSize = 2097152000; //2000mb

void MallocTest(void*& ptr)
{
    while (1)
    {
        ptr = malloc(maxDataSize);

        if (ptr)
        {
            std::cout << "Malloc success: " << maxDataSize << std::endl;
            return;
        }

        maxDataSize -= 1024;

        if (maxDataSize <= 0)
        {
            return;
        }           
    }
}

int main()
{
    void* ptr = nullptr;
    MallocTest(ptr);
    if (ptr)
    {
        free(ptr);
    }
    system("pause");
    return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)

我使用 Windows 10 x64 和 Visual Studio 2017。当我运行该程序时,我成功获得了 ~1300mb 的 malloc 调用。该程序使用 32 位架构(发布模式)构建,因此理论上内存限制等于 2GB。有什么会导致内存碎片吗?为什么我不能分配超过 ~1300mb ?

R..*_*R.. 6

注意:此答案的大部分内容是在 OP 指出他们使用的操作系统之前编写的。这些概念仍然有效,但具体情况可能有所不同。

你说你的操作系统是 32 位的,确实一个好的32 位实现会有 2 GB 的硬限制,因为大于的对象PTRDIFF_MAX有很多微妙的问题,当你使用它们时很容易导致未定义的行为,并触发编译器错误处理指针运算。但是,您已经达到了一些较低的限制因素。

在 Linux 上,当 32 位进程在 64 位内核上运行时,32 位进程的虚拟地址空间限制在大多数架构上为 3 GB,在某些架构上为 2 GB,而在某些架构上为 4 GB。然而,在这个空间中,已经映射了许多东西,它们将地址空间分割并限制任何连续范围的长度。这包括程序本身,如果它是动态链接的,则包括动态链接器和它使用的任何共享库。

如果您的程序不是构建为 PIE(位置无关可执行文件),则它在 Linux/x86 上的默认基址为 128 MB。这将拆分地址空间的前 128 MB,因为它不能用于连续的上部。库通常在地址空间上限以下加载,从那端分离出一些,但可能不会太多。ASLR(地址空间布局随机化)可能会将它们向下移动一点,但主线内核不会这样做太多,以避免严重碎片化已经非常有限的地址空间。一些为额外加固而打补丁的内核可能会随机化得更多。

最终,在 32 位系统上无法可靠地提供大量连续分配(大于半 GB 左右,甚至可能更少)。如果您需要它们,您可能需要使用 64 位系统。但更好的选择可能是找出不需要连续分配的替代数据结构,甚至可以处理不适合内存的数据。