内存需求:C ++中的堆与堆栈

时间:2014-08-20 05:48:18

标签: c++ memory-management heap-memory stack-memory

所以今晚我有一次奇怪的经历。

我正在开发一个C ++程序,它需要某种方式从文件中读取一长串简单数据对象并将它们存储在主内存中,大约有400,000个条目。对象本身就像:

class Entry
{
public:
    Entry(int x, int y, int type);
    Entry(); ~Entry();
    // some other basic functions
private:
    int m_X, m_Y;
    int m_Type;
};

简单,对吧?好吧,因为我需要从文件中读取它们,所以我有一些像

这样的循环
Entry** globalEntries;
globalEntries = new Entry*[totalEntries];
entries = new Entry[totalEntries];// totalEntries read from file, about 400,000
for (int i=0;i<totalEntries;i++)
{
    globalEntries[i] = new Entry(.......);
}

当我在任务管理器上跟踪程序时,该程序的添加为程序增加了大约25到35兆字节。对堆栈分配的简单更改:

Entry* globalEntries;
globalEntries = new Entry[totalEntries];
for (int i=0;i<totalEntries;i++)
{
    globalEntries[i] = Entry(.......);
}

突然间它只需要3兆字节。为什么会这样?我知道指针对象对它们有一点额外的开销(指针地址为4个字节),但它不足以使这有很大的不同。可能是因为程序没有效率地分配内存,最终在分配的内存之间分配了大量未分配的内存?

2 个答案:

答案 0 :(得分:2)

你的代码错了,或者我不知道这是如何运作的。使用new Entry [count],您可以创建一个新的Entry数组(类型为Entry*),但是您将其分配给Entry**,因此我假设您使用了new Entry*[count]。< / p>

您接下来要做的是在堆上创建另一个新的Entry对象,并将其存储在globalEntries数组中。因此,您需要400,000个指针+ 400.000个元素的内存。 400,000个指针在64位计算机上占用3 MiB内存。此外,您有400.000单个Entry分配,这将需要sizeof (Entry)加上可能更多的内存(对于内存管理器 - 它可能必须存储分配的大小,关联的池,对齐/填充等。这些额外的簿记记忆可以快速加起来。

如果您将第二个示例更改为:

 Entry* globalEntries;
 globalEntries = new Entry[count];
 for (...) {
     globalEntries [i] = Entry (...);
 }

内存使用量应该等于堆栈方法。

当然,理想情况下,您会使用std::vector<Entry>

答案 1 :(得分:1)

首先,在没有指定您正在观看的列的情况下,任务管理器中的数字没有任何意义。在现代操作系统上,甚至难以定义你的意思&#34;使用内存&#34; - 我们在谈论私人网页吗?工作集?只留在RAM中的东西?保留但未提交内存计数?谁为流程之间共享的内存付费?是否包含内存映射文件?

如果您正在观看一些有意义的指标,则无法看到使用3 MB内存 - 您的对象至少为12个字节(假设32位整数且没有填充),因此400000个元素将需要大约4.58 MB 。另外,如果它与堆栈分配有关,我会感到惊讶 - VC ++中的默认堆栈大小是1 MB,你应该已经有堆栈溢出。

无论如何,期望使用不同的内存是合理的:

  • 堆栈(大部分)从一开始就被分配,因此即使没有真正用于任何事情,你的名义上也会消耗你的内存(实际上虚拟内存和自动堆栈扩展使这有点复杂,但它&# 39; s&#34;足够真实&#34;);
  • CRT堆对任务管理器是不透明的:它看到的只是操作系统给进程的内存,而不是C堆的内容&#34;真的&#34;正在使用;堆增长(请求内存到操作系统)不仅仅是为进一步的内存请求做好准备所必需的 - 所以你看到的是没有进一步系统调用就可以提供多少内存;
  • 您的&#34;单独分配&#34;方法有很大的开销。您使用new Entry[size]获得size*sizeof(Entry)字节的所有连续数组加上堆簿记数据(通常是几个整数字段);分离的分配方法成本至少为size*sizeof(Entry)(所有&#34;裸元素的大小&#34;)加size*sizeof(Entry *)(指针数组的大小)加上size+1乘以成本每个分配。如果我们假设每个分配的成本为2英特的32位架构,您很快就会发现这会占用size*24+8个字节的内存,而不是堆中连续数组的size*12+8;
  • 堆通常会给出不是你所要求的大小的块,因为它管理固定大小的块;因此,如果您分配单个对象,您可能也需要支付一些额外的填充 - 假设它有16个字节的块,您通过分别分配它们为每个元素额外支付4个字节;这将存储器估计移出size*28+8,即每个12字节元素的开销为16字节。