我有一个非常基本的应用程序,归结为以下代码:
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下面列出),尽管它的价值与我所期待的无关。真正为我抛出的东西是Heap值,这是明显巨大的尺寸来源。
我不明白为什么会这样?根据{{3}},如果我已经正确理解它,gBigArray将被放置在数据或bss段中 - 但是我猜测每个元素都是未初始化的指针,它将被放置在bss段中。那么为什么堆值会比所要求的更大呢?
答案 0 :(得分:4)
如果你知道内存分配器是如何工作的,那听起来并不傻。它们跟踪分配的块,因此存在一个存储大小的字段以及指向下一个块的指针,甚至可能是一些填充。有些编译器会在调试版本中的已分配区域周围放置保护空间,因此如果您在分配区域之外或之前编写,程序可以在尝试释放分配的空间时在运行时检测到它。
答案 1 :(得分:2)
您一次分配一个字符。每个分配通常有一个空间开销
在一个大块(或至少在几个块)中分配内存
答案 2 :(得分:1)
不要忘记char* gBigArray[200][200][200];
为每个字大小的200*200*200=8000000
指针分配空间。这是32位系统上的32 MB。
为另外8MB添加另一个8000000
char
。由于你是逐个分配它们,它可能无法在每个项目的一个字节上分配它们,因此它们可能也会采用每个项目的字大小,从而产生另一个32MB(32位系统)。
其余的可能是开销,这也很重要,因为C ++系统必须记住用new
分配的数组包含delete []
的数量。
答案 3 :(得分:1)
Owww!如果面对该代码,我的嵌入式系统会翻身并死掉。每个分配都有相关的额外信息,并且间隔为固定大小,或通过链表类型对象进行管理。在我的系统上,1个char将成为小型对象分配器中的64字节分配,这样管理将处于O(1)时间。但是在其他系统中,这可能会很容易地破坏你的内存,使后续的新的和删除运行速度非常慢O(n)其中n是它跟踪的事物的数量,并且随着时间的推移随着时间的推移随着每个char变为至少32字节的分配,并放在内存中的各种小型空洞中,从而推动你的分配堆远远超出你的预期。
如果您需要使用贴图新指针或其他指针技巧,请执行单个大型分配并将3D数组映射到其上。
答案 4 :(得分:0)
一次分配1个字符可能更贵。每个分配都有元数据标题,因此字符的1个字节小于标题元数据,因此您可以通过执行一次大的分配(如果可能)来节省空间,这样可以减轻每个具有自己元数据的单独分配的开销。
答案 5 :(得分:0)
也许这是记忆大步的问题?值之间有多大的差距?
答案 6 :(得分:0)
30 MB用于指针。其余的是您使用new
调用分配的存储,指针指向到。允许编译器出于各种原因分配多个字节,比如在字边界上对齐,或者在以后需要的时候给出一些增长的空间。如果您想要8 MB的字符,请将*
从gBigArray
的声明中删除。
答案 7 :(得分:0)
从上述帖子编辑到社区维基帖:
正如下面的答案所说,这里的问题是我创建一个新的char 200 ^ 3次,虽然每个char只有1个字节,但是堆上的每个对象都有开销。似乎为所有字符创建了一个char数组,将内存降低到更可信的水平:
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);
}