10

我正在玩耍以了解可以分配多少内存。最初我认为可以分配的最大内存等于物理内存(RAM)。我通过运行如下所示的命令在 Ubuntu 12.04 上检查了我的 RAM:

~$ free -b
             total       used       free     shared    buffers     cached
Mem:    3170848768 2526740480  644108288          0  265547776 1360060416
-/+ buffers/cache:  901132288 2269716480
Swap:   2428497920          0 2428497920

如上所示,总物理内存为 3Gig(3170848768 字节),其中只有 644108288 字节是空闲的,所以我假设我最多只能分配这么多内存。我通过编写仅以下两行的小程序对其进行了测试:

char * p1 = new char[644108290] ;
delete p1;

由于代码运行完美,这意味着它成功分配了内存。此外,我尝试分配大于可用物理空闲内存的内存,但它没有引发任何错误。然后每个问题

malloc 可以分配的最大内存

我认为它必须使用虚拟内存。所以我测试了免费交换内存的代码,它也有效。

char * p1 = new char[2428497920] ;
delete p1;

我试图分配空闲交换加上空闲 RAM 字节的内存

char * p1 = new char[3072606208] ;
delete p1;

但是这次代码抛出bad_alloc异常失败。为什么这次代码不起作用。

现在我在一个新程序中在编译时分配内存,如下所示:

char p[3072606208] ;
char p2[4072606208] ;
char p3[5072606208];
cout<<"Size of array p = " <<sizeof p <<endl;
cout<<"Size of array p2 = " <<sizeof p2<<endl;
cout<<"Size of array p2 = " <<sizeof p3;

输出显示

Size of array p = 3072606208
Size of array p1 = 4072606208
Size of array p2 = 777638912

你能帮我理解这里发生了什么吗?为什么它允许在编译时分配内存而不是动态分配。p当分配编译时间时,如何p1能够分配大于交换内存和可用 RAM 内存的内存。哪里p2失败了。这究竟是如何工作的。这是一些未定义的行为还是特定于操作系统的行为。谢谢你的帮助。我正在使用 Ubuntu 12.04 和 gcc 4.6.3。

4

5 回答 5

8

在您使用它们之前,内存页面实际上并未映射到您的程序。所做malloc的只是保留一定范围的虚拟地址空间。在您尝试读取或写入它们之前,没有物理 RAM 映射到这些虚拟页面。

即使您分配全局或堆栈(“自动”)内存,在您触摸它们之前也没有物理页面的映射。

最后,sizeof()在编译时进行评估,此时编译器不知道操作系统稍后会做什么。所以它只会告诉你对象的预期大小。

memset如果您在每种情况下都尝试将内存设为 0,您会发现事情的表现会非常不同。此外,您可能想尝试calloc将其内存归零。

于 2012-10-25T18:19:02.140 回答
2

有趣....要注意的一件事:当你写

char p[1000];

您在堆栈上分配(好吧,保留)100 个字节。

当你写

char* p = malloc(100);

您在堆上分配 100 个字节。巨大差距。现在我不知道为什么堆栈分配正在工作 - 除非 [] 之间的值被编译器读取为 int 并因此环绕以分配一个更小的块。

大多数操作系统无论如何都不会分配物理内存,它们会从虚拟地址空间中为您提供页面,这些页面在您使用它们之前一直未使用(因此未分配),然后 CPU 的内存管理器单元将介入为您提供内存要求。尝试写入您分配的那些字节,看看会发生什么。

此外,至少在 Windows 上,当您分配内存块时,您只能保留操作系统可用的最大连续块 - 因此当内存因重复分配而碎片化时,您可以 malloc 减少的最大侧块。我不知道Linux是否也有这个问题。

于 2012-10-25T18:24:00.560 回答
2

这两个程序之间存在巨大差异:

程序1.cpp

int main () {
   char p1[3072606208];
   char p2[4072606208];
   char p3[5072606208];

   std::cout << "Size of array p1 = " << sizeof(p1) << std::endl;
   std::cout << "Size of array p2 = " << sizeof(p2) << std::endl;
   std::cout << "Size of array p3 = " << sizeof(p3) << std::endl;
}

程序2.cpp:

char p1[3072606208];
char p2[4072606208];
char p3[5072606208];

int main () {

   std::cout << "Size of array p1 = " << sizeof(p1) << std::endl;
   std::cout << "Size of array p2 = " << sizeof(p2) << std::endl;
   std::cout << "Size of array p3 = " << sizeof(p3) << std::endl;
}

第一个在堆栈上分配内存;由于堆栈溢出,它将出现分段错误。第二个根本没有多大作用。那个记忆还不完全存在。它以未触及的数据段的形式出现。让我们修改第二个程序,以便触摸数据:

char p1[3072606208];
char p2[4072606208];
char p3[5072606208];

int main () {

   p1[3072606207] = 0;
   p2[3072606207] = 0;
   p3[3072606207] = 0;

   std::cout << "Size of array p1 = " << sizeof(p1) << std::endl;
   std::cout << "Size of array p2 = " << sizeof(p2) << std::endl;
   std::cout << "Size of array p3 = " << sizeof(p3) << std::endl;
}

这不会为p1p2p3在堆或堆栈上分配内存。该内存位于数据段中。它是应用程序本身的一部分。这有一个大问题:在我的机器上,这个版本甚至不会链接。

于 2012-10-25T19:00:06.707 回答
1

首先要注意的是,在现代计算机中,进程不能直接访问 RAM(在应用程序级别)。相反,操作系统将为每个进程提供“虚拟地址空间”。操作系统拦截访问虚拟内存的调用,并在需要时保留实际内存。

所以当mallocornew说它为你找到了足够的内存时,它只是意味着它在虚拟地址空间中为你找到了足够的内存。您可以通过运行以下程序来检查这一点,memset并将其注释掉。(小心,这个程序使用了一个繁忙的循环)。

#include <iostream>
#include <new>
#include <string.h>

using namespace std;

int main(int argc, char** argv) {

    size_t bytes = 0x7FFFFFFF;
    size_t len = sizeof(char) * bytes;
    cout << "len = " << len << endl;

    char* arr = new char[len];
    cout << "done new char[len]" << endl;

    memset(arr, 0, len); // set all values in array to 0
    cout << "done setting values" << endl;

    while(1) {
        // stops program exiting immediately
        // press Ctrl-C to exit
    }

    return 0;
}

memset是程序的一部分时,您会注意到计算机使用的内存大量跳跃,如果没有它,您几乎不会注意到任何差异(如果有的话)。当memset它被调用时,访问数组的所有元素,迫使操作系统在物理内存中提供可用空间。因为 new 的参数是 a size_t(见这里),那么你可以调用它的最大参数是2^32-1,尽管这不能保证成功(它肯定不会在我的机器上)。

至于您的堆栈分配:David Hammem 的回答说得比我好。我很惊讶你能够编译这些程序。使用与您相同的设置(Ubuntu 12.04 和 gcc 4.6)我得到如下编译错误:

test.cpp:在函数'int main(int,char**)'中:

test.cpp:14:6:错误:变量“arr”的大小太大

于 2012-10-25T19:54:13.250 回答
0

试试下面的代码:

bool bExit = false;
unsigned int64 iAlloc = 0;

do{
   char *test = NULL;
   try{
        test = new char[1]();
        iAlloc++;
   }catch(bad_alloc){
   bExit = true;}
}while(!bExit);

char chBytes[130] = {0};
sprintf(&chBytes, "%d", iAlloc);
printf(&chBytes);

在一次运行中不要打开其他程序,在另一次运行中在使用内存映射文件的应用程序中加载一些大文件。

可能有助于您理解。

于 2012-10-25T18:47:01.857 回答