11

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

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

int main()
{
   int *pointer;
}

在此处输入图像描述

  • 它占用了 144KB。

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

int main()
{
   int *pointer;

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

在此处输入图像描述

  • 然后它占用了 (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

在此处输入图像描述

内存使用量随着整数的数量呈指数增长。
为什么会这样?

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

注意:sizeof(integer)退货4

4

9 回答 9

15

单独分配的动态对象的内存不需要是连续的。事实上,由于对齐要求new char[N](即在 处对齐alignof(std::maxalign_t),通常为 16),标准内存分配器可能永远不会费心返回除16字节对齐内存之外的任何内容。所以每个int分配实际上消耗(至少)16 个字节。(分配器可能需要更多内存用于内部簿记。)

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

于 2013-05-18T12:52:00.123 回答
10

Non-optimized allocations in common allocators go with some overhead. You can think of two "blocks": An INFO and a STORAGE block. The Info block will most likely be right in front of your STORAGE block.

So if you allocate you'll have something like that in your memory:

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

Additionally the block will be aligned along a certain granularity (somewhat like 16 bytes in case of int).

I'll write about how this looks like on MSVC12, since I can test on that in the moment.

Let's have a look at our memory. The arrows indicate 16 byte boundaries.

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

If you allocate a single 4 byte integer, you'll get 4 bytes of memory at a certain 16 bytes boundary (the orange square after the second boundary). The 16 bytes prepending this block (the blue ones) are occupied to store additional information. (I'll skip things like endianess etc. here but keep in mind that this can affect this sort of layout.) If you read the first four bytes of this 16byte block in front of your allocated memory you'll find the number of allocated bytes.

If you now allocate a second 4 byte integer (green box), it's position will be at least 2x the 16 byte boundary away since the INFO block (yellow/red) must fit in front of it which is not the case at the rightnext boundary. The red block is again the one that contains the number of bytes.

As you can easily see: If the green block would have been 16 bytes earlier, the red and the orange block would overlap - impossible.

You can check that for yourself. I am using MSVC 2012 and this worked for me:

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;

prints

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

And that is perfectly correct. Therefore a memory allocation using new has in case of MSVC12 an additional "INFO" block which is at least 16 bytes in size.

于 2013-05-18T13:21:42.263 回答
4

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

试试看pointer = new int[262144];有什么不同。

于 2013-05-18T12:52:35.330 回答
2

每次分配一个int

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

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

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

于 2013-05-18T13:15:12.340 回答
1

You are allocating more than just an int, you are also allocating a heap block which has overhead (which varies by platform). Something needs to keep track of the heap information. If you instead allocated an array of ints, you'd see memory usage more in line with your expectations.

于 2013-05-18T13:21:21.707 回答
1

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

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

对于更多变量,当您分配一些内存(内存对齐)时,内存并不是真正连续的,因此它可能会占用比您想象的更多的内存。

于 2013-05-18T12:54:11.043 回答
1

两种解释:

  1. 动态内存分配(在堆上)不一定是连续的。使用new时执行动态分配。
  2. 如果您包含调试符号(-g编译器标志),您的内存使用量可能比预期的要大。
于 2013-05-18T12:54:34.963 回答
1

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

于 2013-05-18T12:57:39.337 回答
1

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

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

尝试在下运行您的程序strace并查找对 的调用brk,并注意每次请求的大小。

于 2013-05-18T13:32:25.167 回答