为什么 VS Debug build 分配的变量相隔这么远?
Why does VS Debug build allocates variables so far apart?
我正在使用 Visual Studio 2019,我注意到在调试版本中,分配的变量彼此相距很远。我查看了 Project Properties 并尝试在线搜索但找不到任何内容。我在 Debug 和 Release 模式下 运行 下面的代码,这里是各自的输出。
int main() {
int a = 3;
int b = 5;
int c = 8;
int d[5] = { 10,10,10,10,10 };
int e = 14;
std::cout << "a: " << &a
<< "\nb: " << &b
<< "\nc: " << &c
<< "\nd_start: " << &d[0]
<< "\nd_end: " << &d[4] + 1
<< "\ne: " << &e
<< std::endl;
}
正如您在下面看到的,变量按照您的预期分配(一个接一个),中间没有浪费内存。甚至最后一个变量 e
也被优化为插入 c 和 d 之间。
// Release_x64 Build Ouput
a: 0000003893EFFC40
b: 0000003893EFFC44
c: 0000003893EFFC48
d_start: 0000003893EFFC50
d_end: 0000003893EFFC64
e: 0000003893EFFC4C // e is optimized in between c and d
下面是让我感到困惑的输出。在这里你可以看到 a
和 b
被分配了 32 个字节!所以它们之间有28字节的wasted/uninitialized内存。除了 int d[5]
之外的其他变量也会发生同样的事情。 d
在 c
之后有 32 个未初始化字节,但在 e
之前只有 24 个未初始化字节。
// Debug_x64 Build Output
a: 00000086D7EFF3F4
b: 00000086D7EFF414
c: 00000086D7EFF434
d_start: 00000086D7EFF458
d_end: 00000086D7EFF46C
e: 00000086D7EFF484
我的问题是为什么会这样?为什么 MSVC 将这些变量分配得如此之远,什么决定了将它们分开多少 space 以便数组不同?
调试版本分配存储的方式与发布版本不同。特别是debug版本在每个存储块的开始和结束分配了一些space,所以它的分配模式有些不同
调试分配器还会检查它分配的块开始和结束处的存储,看它是否以任何方式损坏。
存储以量化块的形式分配,其中量程未指定,但大约为 16 或 32 字节。因此,如果您分配了一个包含六个元素的 DWORD 数组(大小 = 6 * sizeof(DWORD) 字节 = 24 字节),那么分配器实际上将传送 32 个字节(一个 32 字节量程或两个 16 字节量程)。因此,如果您写入元素 [6](第七个元素),则会覆盖一些 "dead space",并且不会检测到错误。但是在release版本中,quantum可能是8字节,会分配三个8字节的quanta,写入数组的[6]元素会覆盖属于下一个chunk的存储分配器数据结构的一部分。在那之后一切都是下坡路。在程序退出之前,错误甚至可能不会出现!您可以为任何大小的量程构造类似的 "boundary condition" 情况。因为分配器的两个版本的量程大小相同,但分配器的调试版本为自己的目的添加了隐藏 space,你将在调试和释放模式下获得不同的存储分配模式。
我正在使用 Visual Studio 2019,我注意到在调试版本中,分配的变量彼此相距很远。我查看了 Project Properties 并尝试在线搜索但找不到任何内容。我在 Debug 和 Release 模式下 运行 下面的代码,这里是各自的输出。
int main() {
int a = 3;
int b = 5;
int c = 8;
int d[5] = { 10,10,10,10,10 };
int e = 14;
std::cout << "a: " << &a
<< "\nb: " << &b
<< "\nc: " << &c
<< "\nd_start: " << &d[0]
<< "\nd_end: " << &d[4] + 1
<< "\ne: " << &e
<< std::endl;
}
正如您在下面看到的,变量按照您的预期分配(一个接一个),中间没有浪费内存。甚至最后一个变量 e
也被优化为插入 c 和 d 之间。
// Release_x64 Build Ouput
a: 0000003893EFFC40
b: 0000003893EFFC44
c: 0000003893EFFC48
d_start: 0000003893EFFC50
d_end: 0000003893EFFC64
e: 0000003893EFFC4C // e is optimized in between c and d
下面是让我感到困惑的输出。在这里你可以看到 a
和 b
被分配了 32 个字节!所以它们之间有28字节的wasted/uninitialized内存。除了 int d[5]
之外的其他变量也会发生同样的事情。 d
在 c
之后有 32 个未初始化字节,但在 e
之前只有 24 个未初始化字节。
// Debug_x64 Build Output
a: 00000086D7EFF3F4
b: 00000086D7EFF414
c: 00000086D7EFF434
d_start: 00000086D7EFF458
d_end: 00000086D7EFF46C
e: 00000086D7EFF484
我的问题是为什么会这样?为什么 MSVC 将这些变量分配得如此之远,什么决定了将它们分开多少 space 以便数组不同?
调试版本分配存储的方式与发布版本不同。特别是debug版本在每个存储块的开始和结束分配了一些space,所以它的分配模式有些不同
调试分配器还会检查它分配的块开始和结束处的存储,看它是否以任何方式损坏。
存储以量化块的形式分配,其中量程未指定,但大约为 16 或 32 字节。因此,如果您分配了一个包含六个元素的 DWORD 数组(大小 = 6 * sizeof(DWORD) 字节 = 24 字节),那么分配器实际上将传送 32 个字节(一个 32 字节量程或两个 16 字节量程)。因此,如果您写入元素 [6](第七个元素),则会覆盖一些 "dead space",并且不会检测到错误。但是在release版本中,quantum可能是8字节,会分配三个8字节的quanta,写入数组的[6]元素会覆盖属于下一个chunk的存储分配器数据结构的一部分。在那之后一切都是下坡路。在程序退出之前,错误甚至可能不会出现!您可以为任何大小的量程构造类似的 "boundary condition" 情况。因为分配器的两个版本的量程大小相同,但分配器的调试版本为自己的目的添加了隐藏 space,你将在调试和释放模式下获得不同的存储分配模式。