试验C - 为什么我不能分配和使用2GB的内存?

时间:2015-11-29 06:53:00

标签: c windows malloc

我继续尝试C.我有这个程序可以让你决定你想吃多少RAM。

char * eatRAM()
{
    unsigned long long toEat;
    unsigned long long i = 0;
    float input;
    char * pMemory = NULL;
    int megaByte = 1048576;

    puts("How much RAM do you want to eat? (in Mega Bytes)");
    puts("NOTE: If you want to eat more RAM than you have available\nin your system, the program will crash");
    printf("\n>> MB: ");
    scanf("%f", &input);

    toEat = (unsigned long long)(input * megaByte);
    pMemory = malloc(toEat);

    printf("\n\nEating in total: %llu Bytes\n", toEat);
    puts("Check your task manager!\n");

    if(pMemory != NULL)
    {
        printf("\n\nEating in total: %llu Bytes\n", toEat);
        puts("Check your task manager!\n");

        for(i; i < toEat; i++)
        {
            pMemory[i] = 'x';
        }
    }
    else
    {
        puts("\nSeems like that amount of memory couldn't be allocated :( \n");
    }
    return pMemory;
}

更新的问题:

问题是......如果我输入例如 1024MB 它可以工作,我可以在任务管理器中看到它使用1GB的RAM。即使我输入 1500MB 也可以。

但如果我输入 2048MB ,则说

  

似乎无法分配大量内存:(

或即使我输入 1756MB

记住我是C的新手,也许我省略了一些与操作系统允许我访问内存有关的重要内容,它可能是什么?

3 个答案:

答案 0 :(得分:6)

Windows上的32位进程默认提供2 GB的可用地址空间。完整pow(2,32)地址空间的下半部分,操作系统使用前2 GB。由于几乎没有人使用32位操作系统,当您将程序与/ LARGEADDRESSAWARE链接时,可以获得4 GB。

代码和数据需要共享2 GB VM空间。您的程序通常加载0x00400000,您使用的任何操作系统DLL(如kernel32.dll和ntdll.dll)都具有高负载地址(超过0x7F000000)。至少启动线程的堆栈和默认进程堆是在程序开始运行之前创建的,它们的地址通常是不可预测的。

您的程序在大多数操作系统安装中都会受到收缩包装病毒攻击,您将注入DLL,提供反恶意软件和云存储等“服务”。这些DLL的加载地址是不可预测的。您自己链接的任何DLL都会在程序启动时隐式加载。很少有程序员注意他们喜欢的基址,并将其保留为默认值0x1000000。您可以从调试器的“模块”窗口中看到这些DLL。这样的DLL通常有自己的CRT,并倾向于创建自己的堆。

您自己进行的分配,尤其是不会来自低碎片堆的非常大的分配,需要在现有代码和数据分配之间的孔中找到地址空间。如果你得到1500 MB,那么你的VM非常干净。一般情况下,你会遇到超过650 MB的麻烦,当程序运行一段时间并且虚拟机空间分散时,会迅速减少。分配失败几乎总是因为操作系统无法找到足够大的漏洞,而不是因为您没有足够的虚拟机。这些洞的总和可能比失败的分配请求大得多。

这些细节正在迅速成为一个民间故事,仍有很少的原因仍然针对x86。目标x64和地址空间碎片在接下来的20年内不会成为问题,非常难以分割8TB的VM。有足够的空间来增长。

所以很明显为什么你不能得到2048 MB,你无法得到它。从SysInternals的VMMap utility获得进一步的见解,它向您展示了VM是如何被分割的。而Mark Russinovich'blog post和书给出了很多背景知识。

答案 1 :(得分:4)

这是操作系统限制,而不是C限制。

要解决超过4Gb系统范围的问题,您需要运行64位操作系统,并且对于单个进程来解决超过4Gb的问题,它必须构建为64位应用程序。 Win32每进程内存限制为2Gb。由于内存是虚拟化的,5Gb的物理RAM在很大程度上是无关紧要的。

除了32位和64位系统和应用程序的理论限制外,操作系统仍可能施加限制。例如,Windows的不同版本和版本(Home,Pro,Server等)由于商业原因而强加了特定限制。

您的案例中的具体答案需要有关您的系统,工具链和构建选项的信息。如果您使用的是Windows和VC ++,则需要考虑/LARGEADDRESAWARE选项;它在32位编译器中默认不启用,但Win32在任何情况下都有2Gb默认限制,除非启用physical address extension

我相信在Win64上运行的32位进程可以解决整个4Gb 32位地址空间,但在这种情况下你肯定需要用/LARGEADDRESAWARE进行构建。即使这样,并不是所有的空间都可用于堆,并且任何单个分配必须是连续的,因此可能受到先前分配和堆碎片的限制。

答案 2 :(得分:0)

如果剩余可用内存量小于您尝试分配的数量,则分配将永远不会有效。

此外,就在这一行之后:

pMemory = (char *) malloc(toEat);

添加以下内容:

if (!pMemory){
  printf("Can't allocate memory\n");
  return NULL;
}

这样,您将看到一个“无法分配内存”消息而不是接收“分段错误”相关消息,而您的函数将返回NULL。

确保在调用eatRam函数的函数中执行类似的值检查,否则您将收到“细分错误”消息。而且,使用像gdb这样的调试器。