是否存在与堆内存分配相关的内存开销(例如堆中的标记)?

Cod*_*Dan 8 c++ windows heap memory-management visual-studio

使用最近的Visual Studio C++编译器在Windows上特别考虑C++,我想知道实现:

假设我正在使用发布编译器,并且我不关心内存碎片/打包问题,是否存在与在堆上分配内存相关的内存开销?如果是这样,大概每个分配的字节数可能是多少?它会比64-bit代码更大32-bit吗?

我对现代实现并不是很了解,但我想知道每次分配是否有写入堆的标记,或者是否维护某种表(如文件分配表).

在一个相关点(因为我主要考虑标准库功能,如'map'),Microsoft标准库实现是否曾使用自己的分配器(对于像树节点这样的东西)来优化使用?

Mat*_*son 7

是的,一点没错.

分配的每个内存块将具有"标头"的恒定开销,以及小的可变部分(通常在末尾).究竟有多少取决于使用的确切C运行时库.在过去,我通过实验发现每个分配大约32-64字节.可变部分是为了应对对齐 - 每个内存块将与一些不错的甚至2 ^ n基址对齐 - 通常为8或16个字节.

我不熟悉内部设计std::map或类似工作的方式,但我非常怀疑它们在那里有特殊的优化.

您可以通过以下方式轻松测试开销:

char *a, *b;

a = new char;
b = new char;

ptrdiff_t diff = a - b;

cout << "a=" << a << " b=" << b << " diff=" << diff;
Run Code Online (Sandbox Code Playgroud)

[注意到这里可能是大部分常规的学生,上面的ab表达式会调用未定义的行为,因为减去一个已分配的地址和另一个的地址是未定义的行为.这是为了应对不具有线性存储器地址的机器,例如分段存储器或"基于其类型将不同类型的数据存储在位置中".以上应该适用于任何基于x86的操作系统,它不使用具有多个数据段的分段内存模型用于堆 - 这意味着它可以在32位和64位模式下用于Windows和Linux.

您可能希望以不同的类型运行它 - 请记住diff是"类型的数字,所以如果你使它int *a, *b将以"四个字节为单位".你可以制作一个reinterpret_cast<char*>(a) - reinterpret_cast<char *>(b);

[diff可能是负数,如果你在循环中运行它(没有删除ab),你可能会发现突然跳转,其中一大段内存耗尽,运行时库分配了另一个大块]