为啥数组分配后堆这么大
Posted
技术标签:
【中文标题】为啥数组分配后堆这么大【英文标题】:Why is the heap after array allocation so large为什么数组分配后堆这么大 【发布时间】:2011-10-10 14:42:59 【问题描述】:我有一个非常基本的应用程序,可以归结为以下代码:
char* gBigArray[200][200][200];
unsigned int Initialise()
for(int ta=0;ta<200;ta++)
for(int tb=0;tb<200;tb++)
for(int tc=0;tc<200;tc++)
gBigArray[ta][tb][tc]=new char;
return sizeof(gBigArray);
该函数返回 32000000 字节的预期值,大约为 30MB,但在 Windows 任务管理器中(并且假定它不是 100% 准确)给出的内存(私有工作集)值为大约 157MB。我已通过 SysInternals 将应用程序加载到 VMMap 并具有以下值:
我不确定 Image 是什么意思(列在 Type 下),尽管它的值与我的预期无关。真正让我失望的是堆值,这是明显的巨大尺寸的来源。
我不明白为什么会这样?根据this answer,如果我理解正确,gBigArray 将被放置在数据或 bss 段中 - 但是我猜测每个元素都是一个未初始化的指针,它将被放置在 bss 段中。那么为什么堆值会比所需的值大很多呢?
【问题讨论】:
new char
- 你没有创建一个新的单字节字符。您正在创建一个 char 对象,该对象需要考虑元数据和其他开销。该数组只是指向这些对象的 200x200x200 指针,但这些对象本身显然占用了 157-30 = 127 MB 的内存。
为什么首先要有一个 char 指针数组?为什么不只是一个字符数组?
【参考方案1】:
如果您知道内存分配器的工作原理,这听起来并不傻。它们跟踪分配的块,因此有一个存储大小的字段以及指向下一个块的指针,甚至可能是一些填充。一些编译器在调试版本中在分配区域周围放置保护空间,因此如果您在分配区域之外或之前写入,当您尝试释放分配的空间时,程序可以在运行时检测到它。
【讨论】:
【参考方案2】:您一次分配一个字符。每次分配通常都会产生空间开销
将内存分配到一大块(或至少几块)
【讨论】:
【参考方案3】:不要忘记char* gBigArray[200][200][200];
为200*200*200=8000000
指针分配空间,每个字的大小。在 32 位系统上是 32 MB。
再添加一个8000000
char
,再增加8MB。由于您是一个一个地分配它们,因此可能无法按每个项目一个字节分配它们,因此它们可能还会占用每个项目的字大小,从而导致另一个 32MB(32 位系统)。
其余的可能是开销,这也很重要,因为 C++ 系统必须记住分配给 new
的数组包含多少元素 delete []
。
【讨论】:
除非他在这里没有调用new[]
,所以调用delete[]
是未定义的。 delete
的非数组版本通过传入的类型知道大小。【参考方案4】:
哇!如果面对该代码,我的嵌入式系统的东西会翻滚而死。每个分配都有相当多的额外信息与之相关联,或者间隔固定大小,或者通过链表类型对象进行管理。在我的系统上,这个 1 char new 将成为一个小对象分配器的 64 字节分配,这样管理将在 O(1) 时间内完成。但是在其他系统中,这很容易使您的记忆变得非常糟糕,使后续的新操作和删除操作运行得非常缓慢 O(n) 其中 n 是它跟踪的事物的数量,并且随着时间的推移通常会给应用程序带来厄运,因为每个字符都会变成至少 32 字节的分配并被放置在内存中的各种小孔中,从而使您的分配堆超出您的预期。
如果您需要使用放置 new 或其他指针技巧,请进行一次大分配并将 3D 数组映射到它上面。
【讨论】:
【参考方案5】:一次分配 1 个字符可能更昂贵。每个分配都有元数据标头,因此一个字符的 1 个字节小于标头元数据,因此您实际上可以通过进行一次大分配(如果可能)来节省空间,这样您就可以减轻每个具有自己元数据的单独分配的开销。
【讨论】:
【参考方案6】:也许这是内存跨度的问题?值之间的差距有多大?
【讨论】:
【参考方案7】:30 MB 用于 指针。其余的用于您使用new
调用分配的存储空间,指针指向to。出于各种原因,编译器可以分配多个字节,例如对齐字边界,或者提供一些增长空间以防您以后需要它。如果您想要 8 MB 的字符,请将 *
从声明中删除为 gBigArray
。
【讨论】:
【参考方案8】:将上述帖子编辑为社区 wiki 帖子:
正如下面的答案所说,这里的问题是我正在创建一个新的 char 200^3 次,虽然每个 char 只有 1 个字节,但堆上的每个对象都有开销。似乎为所有字符创建一个字符数组会将内存降低到更可信的水平:
char* gBigArray[200][200][200];
char* gCharBlock=new char[200*200*200];
unsigned int Initialise()
unsigned int mIndex=0;
for(int ta=0;ta<200;ta++)
for(int tb=0;tb<200;tb++)
for(int tc=0;tc<200;tc++)
gBigArray[ta][tb][tc]=&gCharBlock[mIndex++];
return sizeof(gBigArray);
【讨论】:
以上是关于为啥数组分配后堆这么大的主要内容,如果未能解决你的问题,请参考以下文章