是否存在与堆内存分配相关的内存开销(例如堆中的标记)? 特别是C++上使用最近的VisualStudioC++编译器的Windows思想,我想知道堆的实现:

是否存在与堆内存分配相关的内存开销(例如堆中的标记)? 特别是C++上使用最近的VisualStudioC++编译器的Windows思想,我想知道堆的实现:,c++,windows,visual-studio,memory-management,heap,C++,Windows,Visual Studio,Memory Management,Heap,假设我使用的是发行版编译器,并且我不关心内存碎片/打包问题,那么是否存在与在堆上分配内存相关的内存开销?如果是这样的话,每个分配大概有多少字节? 在64位代码中它会比32位代码大吗 我对现代堆实现了解不多,但我想知道每次分配时是否有标记写入堆中,或者是否维护某种表(如文件分配表) 在一个相关的问题上(因为我主要考虑的是像“map”这样的标准库功能),Microsoft标准库实现是否使用过自己的分配器(对于树节点之类的东西)来优化堆的使用?是的,绝对如此 分配的每个内存块都有一个恒定的“头”开销,

假设我使用的是发行版编译器,并且我不关心内存碎片/打包问题,那么是否存在与在堆上分配内存相关的内存开销?如果是这样的话,每个分配大概有多少字节? 在
64位
代码中它会比
32位
代码大吗

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

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

是的,绝对如此

分配的每个内存块都有一个恒定的“头”开销,以及一个小的可变部分(通常在末尾)。具体数量取决于所使用的确切的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;
char*a、*b;
a=新字符;
b=新字符;
ptrdiff_t diff=a-b;

CUT

Visual C++嵌入了控制缓冲区边界附近的控制信息(链接/大小和可能的校验和)。这也有助于在内存分配和释放期间捕获一些缓冲区溢出

除此之外,您应该记住,
malloc()
需要返回适合所有基本类型(
char
int
long
double
void*
void(*)(
)的指针,并且该对齐通常具有最大类型的大小,所以它可能是8个甚至16个字节。如果只分配一个字节,则7到15个字节可能只会丢失到对齐中。我不确定新操作员是否有相同的行为,但很可能是这样


这应该给你一个想法。精确的内存浪费只能通过文档(如果有)或测试来确定。语言标准没有用任何术语对其进行定义。

是。所有实用的动态内存分配器都具有最小的粒度1。例如,如果粒度是16个字节,而您只请求1个字节,那么仍然会分配整个16个字节。如果您要求17个字节,则会分配一个大小为32字节的块,等等

还有一个(相关的)对齐问题

相当多的分配器似乎是大小映射和空闲列表的组合——它们将潜在的分配大小拆分为“bucket”,并为每个bucket保留一个单独的空闲列表。看一看。还有许多其他的分配技术,有各种折衷,但这超出了这里的范围


1通常为8或16字节。如果分配器使用空闲列表,那么它必须在每个空闲插槽中编码两个指针,因此空闲插槽不能小于8字节(32位)或16字节(16位)。例如,如果分配器试图拆分一个8字节的插槽以满足一个4字节的请求,那么剩余的4字节将没有足够的空间来编码空闲列表指针


2例如,如果平台上的
long-long
为8字节,则即使分配器的内部数据结构可以处理小于8字节的块,实际分配较小的块可能会将下一个8字节分配推到未对齐的内存地址。

通常在分配阵列时,有些字节存储在开头,其大小分配给以后的删除。对于大型分配,开销几乎为零。如果你一次分配4个字节,它可以将内存翻一番。我添加了一条注释。如果你有一个更好的方法来确定两个独立分配之间的距离的建议,请随意使用suggets。不,不,我不是在暗示,只是指出了一些东西。为了证明这一点,有时你会求助于UB,没问题:)啊,在你回答原始评论时编辑了我的评论。对不起,其他读者……;)谢谢@Mats。我从未考虑过堆分配器是如此可预测,以至于在大多数情况下,连续(大小相等)分配之间的差异可以可靠地指示内存开销。我可以尝试一下,只要你不在多个线程中运行它,或者在调用new之间调用一些在堆上分配的函数,就可以了。当然,这适用于使用一大块内存来提供不同分配的堆。。。可能有不这样做的分配器。另外,如果在编写这段代码时,您已经对底层堆进行了一系列alloc/free调用,那么它可能会重用这些调用产生的内存块来释放它们——这当然可能是无序的。感谢提供的信息&malloc链接@Branko,这似乎是对堆管理设计的一个非常好的基本介绍。