可以在c ++中动态分配并在编译时分配的最大内存

时间:2012-10-25 18:16:02

标签: c++ memory-management ubuntu new-operator

我正在玩弄了解可以分配多少内存。最初我认为可以分配的最大内存等于物理内存(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;

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

maximum memory which malloc can allocate

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

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

你能帮我理解这里发生的事吗?为什么它允许在编译时分配内存,而不是动态分配内存。 当分配编译时,pp1如何能够分配大于交换加空闲RAM内存的内存。 p2失败的地方。 这究竟是如何运作的。这是一些未定义的行为或操作系统特定的行为。谢谢你的帮助。我使用的是Ubuntu 12.04和gcc 4.6.3。

5 个答案:

答案 0 :(得分:6)

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

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

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

如果在每种情况下尝试将memset内存设置为0,您会发现事情的行为会有很大不同。此外,您可能想尝试calloc,这会将其内存归零。

答案 1 :(得分:2)

有趣......有一点需要注意:写作时

char p[1000];

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

写作时

char* p = malloc(100);

你在堆上分配100个字节。很大的区别。现在我不知道为什么堆栈分配正在工作 - 除非编译器将[]之间的值作为int读取,因此要分配一个更小的块。

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

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

答案 2 :(得分:2)

这两个计划之间存在巨大差异:

program1.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;
}

program2.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分配内存。记忆存在于数据段中。它是应用程序本身的一部分。这有一个很大的问题:在我的机器上,这个版本甚至都不会链接。

答案 3 :(得分:1)

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

因此,当mallocnew表示已为您找到足够的内存时,它只是意味着它在虚拟地址空间中为您找到了足够的内存。您可以通过使用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的参数是size_t(参见here),因此您可以调用它的最大参数是2^32-1,但这并不能保证成功(当然不会在我的机器上。)

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

  

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

     

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

答案 4 :(得分: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);

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

This可以帮助您理解。