C ++为变量分配异常大的amout内存

时间:2013-05-18 12:48:55

标签: c++ memory memory-management integer function-pointers

我最近知道一个整数从内存中占用4个字节。

首先运行此代码,并测量内存使用情况:

int main()
{
   int *pointer;
}

enter image description here

  • 花了144KB。

然后我修改了代码以分配 1000个整数变量

int main()
{
   int *pointer;

   for (int n=0; n < 1000; n++)
     { 
       pointer = new int ; 
     }
}

enter image description here

  • 然后花了(168-144 =)24KB
    但假设占用1000个整数(4bytes x 1000 =)3.9KB

然后我决定使用 262,144个整数变量来消耗1MB的内存

int main()
{
   int *pointer;

   for (int n=0; n < 262144; n++)
     { 
       pointer = new int ; 
     }
}

令人惊讶的是,现在需要8MB

enter image description here

内存使用量,以整数数量呈指数增长 为什么会这样?

我在Kubuntu 13.04(amd64)
请给我一点解释。 谢谢!

注意:sizeof(integer)会返回4

9 个答案:

答案 0 :(得分:14)

单独分配的动态对象的内存不需要是连续的。事实上,由于new char[N]的对齐要求(即在alignof(std::maxalign_t)对齐,通常为16),标准内存分配器可能永远不会为了返回而返回 > 16字节对齐的内存。因此每个int分配实际上消耗(至少)16个字节。 (分配器可能需要进一步的内存来进行内部簿记。)

道德当然是你应该使用std::vector<int>(1000000)来理解一百万个动态整数。

答案 1 :(得分:10)

公共分配器中的非优化分配会带来一些开销。你可以想到两个“块”:一个INFO和一个STORAGE块。 信息块很可能就在您的STORAGE块前面。

所以,如果你分配了你的内存会有类似的东西:

        Memory that is actually accessible
        vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
--------------------------------------------
|  INFO |  STORAGE                         |
--------------------------------------------
^^^^^^^^^
Some informations on the size of the "STORAGE" chunk etc.

此外,块将沿着某个粒度对齐(在int的情况下有点像16个字节)。

我会在MSVC12上写下这个样子,因为我现在可以对它进行测试。

让我们来看看我们的记忆。箭头表示16字节边界。

Memory layout in case of int allocation; each block represents 4 bytes

如果你分配一个4字节整数,你将在某个16字节边界(第二个边界之后的橙色方块)获得4个字节的内存。 在该块之前的16个字节(蓝色的)被占用以存储附加信息。 (我会在这里跳过诸如endianess之类的内容,但请记住,这会影响这种布局。)如果你在分配的内存前读取这个16字节块的前四个字节,你会发现分配的数量字节。

如果现在分配第二个4字节整数(绿色框),它的位置将至少是16字节边界的2倍,因为INFO块(黄色/红色)必须放在它前面,而不是右边界。红色块再次是包含字节数的那个。

您可以很容易地看到:如果绿色块早先是16个字节,则红色和橙色块会重叠 - 不可能。

你可以自己检查一下。我正在使用MSVC 2012,这对我有用:

char * mem = new char[4096];
cout << "Number of allocated bytes for mem is: " << *(unsigned int*)(mem-16) << endl;
delete [] mem;


double * dmem = new double[4096];
cout << "Number of allocated bytes for dmem is: " << *(unsigned int*)(((char*)dmem)-16) << endl;
delete [] dmem;

打印

Number of allocated bytes for mem is: 4096
Number of allocated bytes for dmem is: 32768

这是完全正确的。因此,在MSVC12的情况下,使用new的内存分配有一个额外的“INFO”块,其大小至少为16字节。

答案 2 :(得分:4)

您正在分配多个动态变量。每个变量包含4个字节的数据,但是内存管理器通常存储有关已分配块的一些附加信息,并且每个块应该对齐,这会产生额外的开销。

尝试pointer = new int[262144];并查看差异。

答案 3 :(得分:2)

每次分配int

  • 内存分配器必须为您提供一个16字节对齐的空间块,因为通常,内存分配必须提供合适的对齐,以便内存可用于任何目的。因此,即使请求的数量较少,每个分配通常至少返回16个字节。 (对齐要求可能因系统而异。可以想象,可以优化小分配以减少占用空间。但是,有经验的程序员知道要避免做很多小的分配。)
  • 内存分配器必须使用一些内存来记住分配了多少空间,以便它知道调用free时有多少空间。 (这可以通过结合使用delete运算符的空间知识来优化。但是,一般的内存分配例程通常与编译器的newdelete代码分开。)
  • 内存分配器必须使用一些内存来存储数据结构,以组织有关已分配和释放的内存块的信息。也许这些数据结构需要O(n•log n)空间,因此当有许多小分配时,开销会增加。

另一个可能的影响是,随着内存使用的增长,分配器会从系统请求并初始化更大的块。也许第一次使用初始内存池时,分配器会再请求16个页面。下一次,它请求32.下一次,64。我们不知道内存分配器从系统请求了多少内存实际上已用于满足您对int个对象的请求。

不要动态分配许多小对象。改为使用数组。

答案 4 :(得分:1)

我认为这取决于编译器如何创建输出程序。

程序的内存使用包括程序的所有部分(如.text,其中包含程序的汇编指令),因此在加载时需要一些空间内存。

对于更多变量,当你分配一些内存(内存对齐)时,内存并不是真正连续的,所以它可能需要比你想象的更多的内存。

答案 5 :(得分:1)

两种解释:

  1. 动态内存分配(在堆上)不一定是连续的。使用new时,执行动态分配。
  2. 如果要包含调试符号(-g编译器标志),则内存使用量可能会超出预期。

答案 6 :(得分:1)

每个声明都会生成一个适合编译器对齐选项的新变量,它需要之间的空格(变量的起始地址应该是128或64或32(位)的倍数,这会导致许多变量的内存效率低下vs一个数组)。数组对于连续区域更有用。

答案 7 :(得分:1)

您正在分配的不仅仅是一个int,您还要分配一个具有开销的堆块(根据平台而不同)。有些东西需要跟踪堆信息。如果你改为分配一组int,你会发现内存使用量更符合你的预期。

答案 8 :(得分:1)

除了其他问题中提到的对齐和开销问题之外,这可能是由于C ++运行时请求从OS处理内存分配的方式。

当进程的数据部分填满时,运行时必须为进程分配更多的内存。它可能不会在相同大小的块中执行此操作。一种可能的策略是每次请求内存时,它会增加请求的数量(每次可能使堆大小加倍)。对于不占用大量内存的程序,此策略可以减少内存分配,但会减少大型应用程序请求新分配的次数。

尝试在strace下运行您的计划并查找对brk的来电,并注意每次请求的大小。