6

我测试了这段代码,只是想找出 C++ 实际为 new 运算符保留了多少内存。

#include<iostream>
using namespace std;
int main() {

  cout << "alignment of " << alignof(int) << endl;
  int *intP1 = new int;
  *intP1 = 100;
  cout << "address of intP1 " << intP1 << endl;
  int *intP2 = new int;
  *intP2 = 999;
  cout << "address of intP2 " << intP2 << endl;
  int *intP3 = new int;
  cout << "address of intP3 " << intP3 << endl;
  *intP3 = 333;

  cout << endl;
  cout << (reinterpret_cast<char *>(intP3)-reinterpret_cast<char *>(intP2)) << endl;
  cout << intP3-intP2 << endl;
  cout << endl;

  cout << *(intP1) << endl;
  cout << *(intP1+4) << endl;
  cout << *(intP1+8) << endl;
  cout << *(intP1+16) << endl;
  delete intP1;
  delete intP2;
  delete intP3;
  return 0;
}

在使用 -std=c++11 标志编译代码并运行它之后,这是我从 x86_64 机器上得到的。

    alignment of int4
    address of intP1 = 0xa59010
    address of intP2 = 0xa59030
    address of intP3 = 0xa59050

    the distance of intP3 and intP2 = 32

    intP1 value = 100
    is this a padding value = 0
    intP2 value = 999
    intP3 value = 333

似乎在使用 new 为整数分配 4 字节内存时,它实际上保留了 32 字节块,即 8 个整数的总空间。根据c++对齐的解释,对于64位机器,内存是按16字节对齐的,为什么这里的距离是32字节呢?

有人可以帮我解决这个问题吗?提前致谢。

4

7 回答 7

5

它与对齐无关——它是内部内存分配器工作方式的额外开销。通常,每个内存块在其前面和/或后面都有额外的隐藏信息,用于维护堆的结构。确切的开销会因平台和实施而异。

例如,Doug Leamalloc每次分配的额外开销为 4-8 字节(32 位指针)或 8-16 字节(64 位指针),最小分配大小为 16 字节(32 位)或 32 字节( 64 位)。这意味着即使是 1 字节的分配,内存分配器也需要总共 16 字节的跟踪开销。

于 2012-11-13T23:40:51.827 回答
2

32 字节的差异不仅仅是为了对齐。实际上,请注意地址0xa59010不是 32 对齐的,它只是 16 对齐的。因此,如果它们仅相隔 16 个字节而不是 32 个字节,则地址的对齐不会更糟。

相反,32 字节的差异是内存分配器的开销/效率低下。我怀疑分配器:

  • 有助于为您提供 16 个对齐的地址。这是 128 位 SSE 类型所需要的,所以它对你很有用,但我不知道这是否是分配器是 16 对齐的主要原因,或者它是否只是对分配器方便。
  • 在分配簿记信息之前需要一些空间,这可能是 16 个字节(2 个指针或一个指针和一个大小),但即使不是,它也被舍入到 16 个字节。
  • 实际数据只需要 4 个字节,但由于 16 个字节的簿记和 16 个字节的对齐,分配之间的最小距离为 32 个字节。因此,当您进行 4 字节分配时,会有 12 字节的“松弛空间”/“内部碎片”/“浪费”。

但这只是一个猜测,我没有调查过你使用的任何分配器。

于 2012-11-13T23:42:43.023 回答
1

的调试版本new可以添加相当多的填充以提供保护空间,以便可以检测到一些堆损坏。您应该使用调试版本和发布版本运行它,看看是否有区别。

于 2012-11-13T23:48:47.240 回答
0

你有一个指针和一个内存值。这分配了 2 个内存块,您已经说过每个内存块 16 个字节。2x16=32。

我相信这会给你你想要的结果。

  cout << "alignment of " << alignof(int) << endl;
  int intP1 = 100;
  cout << "address of intP1 " << &intP1 << endl;
  int intP2 = 999;
  cout << "address of intP2 " << &intP2 << endl;
  int intP3 = 333;
  cout << "address of intP3 " << &intP3 << endl;
于 2012-11-13T23:37:37.123 回答
0

int4表示它占用 4 个字节,而不是 4 位。编译器向您显示的所有内容实际上都是准确的!是一些关于原语的文档以及int原语的含义。

是有关如何定义 4 位整数的教程。

让我提一下,这alignof取决于架构。在某些架构int中意味着 16 位 int 或 2 个字节而不是 4 个。

于 2012-11-13T23:38:52.690 回答
0

绝对没有什么需要操作系统来分配三个指针intP1intP2并且intP3彼此相邻。您的代码可能会检测分配中的开销(当然,有一些开销是合理的假设),但不足以证明间距必然是所有分配器开销。

于 2012-11-13T23:48:56.037 回答
0

C++ 标准不保证每个堆分配有多少开销。无论对齐方式如何,分配器通常都会增加额外的开销。分配器从预定大小的桶中完成小分配是很常见的。在这里,最小的存储桶似乎是每次分配 32 个字节,这并不罕见。您很少会在野外发现桶小于 16 字节的分配器。

如果您要分配超过 1 个 int,比如说 int[2],您可能会注意到所占用的内存大小是相同的:32 字节。

另请注意,C++ 标准或分配器不保证 2 个相同大小的分配是连续的。这在大多数情况下可能会受到尊重,但不应依赖。

于 2012-11-13T23:52:33.480 回答