所以今晚我有一次奇怪的经历。
我正在开发一个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个字节),但它不足以使这有很大的不同。可能是因为程序没有效率地分配内存,最终在分配的内存之间分配了大量未分配的内存?
答案 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,你应该已经有堆栈溢出。
无论如何,期望使用不同的内存是合理的:
new Entry[size]
获得size*sizeof(Entry)
字节的所有连续数组加上堆簿记数据(通常是几个整数字段);分离的分配方法成本至少为size*sizeof(Entry)
(所有&#34;裸元素的大小&#34;)加size*sizeof(Entry *)
(指针数组的大小)加上size+1
乘以成本每个分配。如果我们假设每个分配的成本为2英特的32位架构,您很快就会发现这会占用size*24+8
个字节的内存,而不是堆中连续数组的size*12+8
; size*28+8
,即每个12字节元素的开销为16字节。