20,000个小对象的向量与20,000个对象指针的向量到20,000个堆对象

时间:2011-12-29 17:24:48

标签: c++ macos optimization memory-management stl

在OS X Snow Leopard下开发一个32位C ++ / carbon应用程序遇到了一个问题,即在重新分配期间,大约20,000个小对象(每个72个字节)的stl向量失败。看起来这个几兆字节的向量无法扩展到一块连续的内存,在失败时它的大小只有1.2 MB。

GuardMalloc[Appname-33692]: *** mmap(size=2097152) failed (error code=12)
*** error: can't allocate region

GuardMalloc[Appname-35026]: Failed to VM allocate 894752 bytes
GuardMalloc[ Appname-35026]: Explicitly trapping into debugger!!!

#0    0x00a30da8 in GMmalloc_zone_malloc_internal
#1    0x00a31710 in GMmalloc
#2    0x94a54617 in operator new
#3    0x0026f1d3 in __gnu_cxx::new_allocator<DataRecord>::allocate at new_allocator.h:88
#4    0x0026f1f8 in std::_Vector_base<DataRecord, std::allocator<DataRecord> >::_M_allocate at stl_vector.h:117
#5    0x0026f373 in std::vector<DataRecord, std::allocator<DataRecord> >::_M_insert_aux at vector.tcc:275
#6    0x0026f5a6 in std::vector<DataRecord, std::allocator<DataRecord> >::push_back at stl_vector.h:610

我可以想到几个策略:

1)一旦应用程序启动,保留()一个非常非常大的向量。但是,这假设用户可能无法加载对此向量有贡献的其他文件,将其推送到超出预先分配的限制并可能返回到相同的情况。

2)将对象/内存分配的向量更改为指向对象/内存分配的指针向量。显然,使矢量本身更容易管理,但随后会创建20,000个小对象(最终可能会变成50,000个对象,具体取决于用户加载的其他文件)。这是否会产生巨大的开销问题?

3)从向量更改为列表,这可能有其自身的开销问题。

向量不断迭代,通常只附加到。

关于这些问题的任何圣人的想法?

===============

附加注意:此​​特定向量仅保存所有导入的记录,因此可以通过包含排序顺序的另一个向量对它们进行索引和排序。将项目放入此向量后,它将在应用程序的生命周期内保留(通过确保向量中的索引始终对于该特定对象保持不变来帮助支持撤消操作)。

4 个答案:

答案 0 :(得分:3)

我认为在您的情况下,std::dequestd::liststd::vector更合适。 std::list在迭代或随机索引中效率不高,而std::vector调整大小很慢(正如您所观察到的那样)。调整大小时std::deque不需要大量内存,代价是随机索引比矢量稍慢。

答案 1 :(得分:2)

请勿使用vector之类的连续存储空间。转到dequelist,重新分配不会再失败。

如果你真的需要高性能,可以考虑编写自己的容器(即ArrayList)。

答案 2 :(得分:1)

如果即使在堆中,也没有足够的空间,请使用deque; deque在需要时分配不占空间。所以它可以处理1.2 MB的限制

deque由一些内存块组成,而不仅仅是一个空间。这就是为什么它可以工作。但它不确定(/完全安全),因为你不控制双端队列的行为。

请参阅this有关内存碎片的信息(以下是从网页上复制/粘贴): http://www.design-reuse.com/articles/25090/dynamic-memory-allocation-fragmentation-c.html

内存碎片

理解内存碎片的最佳方法是查看示例。对于这个例子,假设有10K堆。首先,要求3K的面积,因此:

     #define K (1024)
     char *p1;
     p1 = malloc(3*K);

然后,再请求4K:

    p2 = malloc(4*K);

3K的内存现已免费。

一段时间后,p1指向的第一个内存分配被解除分配:

    free(p1);

这使得两个3K块中的6K内存空闲。发出进一步的4K分配请求:

   p1 = malloc(4*K);

这会导致失败 - NULL返回到p1 - 因为即使有6K的内存可用,也没有可用的4K连续块。这是内存碎片。

即使对于使用虚拟内存(如osx)的内核,这也是一个问题。

答案 3 :(得分:1)

在你的三个选项中,1似乎不是一个有保证的解决方案,而2增加了复杂性,矢量仍然需要增长。

选项3似乎有点合理(或者可能使用另一个答案中提到的deque)因为虽然它在语义上类似于选项2,但它提供了一种更标准化的分配新数据对象的方法。当然,这假设您只附加数据而不需要随机访问。

然而,所有这一切都说我发现你的程序内存碎片如此糟糕以至于在合理的现代硬件上无法分配1.2MB的内存是令人难以置信的。更有可能的是,程序中潜伏着(或可能是内存泄漏)的某些未定义行为导致它以这种方式运行,无法分配内存。你可以使用valgrind来帮助追捕可能发生的事情。使用内置newdelete而不是GMalloc时是否会出现同样的问题?

您的程序ulimit是否只能访问少量内存?

最后,如果valgrind什么也没找到,而你的程序确实会让内存崩溃,我会考虑退一步并重新考虑你的方法。您可能想要评估一种不依赖于数百万(?)分配的替代方法(我只是看不到少量的分配将堆分割得那么多)。