一个空的堆区域的开销

6

我的工具是Linux、gcc和pthreads。当我的程序从多个线程调用new/delete,并且在堆上存在争用时,会创建“arena”(请参考以下链接http://www.bozemanpass.com/info/linux/malloc/Linux_Heap_Contention.html)。我的程序运行24x7,即使在2周后仍会偶尔创建arena。我认为最终可能会有与线程一样多的arena。ps(1)显示内存消耗令人担忧,但我怀疑只有其中很小一部分实际上被映射。

空的arena的“开销”是多少?(每个arena相比于如果所有分配都限制在传统堆中使用了多少更多的内存?)

是否有任何方法可以强制提前创建n个arena?是否有任何方法可以强制销毁空的arena?


你使用的glibc和gcc版本是什么? - osgx
不同的glibc版本会有不同的答案。 - osgx
你使用ptmalloc吗?gcc和glibc版本是什么? - osgx
最多可以有65k个竞技场。线程没有单独的竞技场,所有线程都共用一个池。 - osgx
尝试:在程序中查找内存泄漏;尽可能多地重用线程;使用TCmalloc(来自Google)。 - osgx
1
@osgx 谢谢你的建议。在发布这个问题之前,我已经修复了内存泄漏问题,并且我知道这是因为传统堆没有增长。 - rleir
5个回答

1

竞技场的销毁...我还不知道,但是有这样的文本(简而言之,它说NO不能销毁/修剪内存)来自于2000年的分析http://www.citi.umich.edu/techreports/reports/citi-tr-00-5.pdf(有点过时)。请告诉我您的glibc版本。

Ptmalloc maintains a linked list of subheaps. To re-
duce lock contention, ptmalloc searchs for the first
unlocked subheap and grabs memory from it to fulfill
a malloc() request. If ptmalloc doesn’t find an
unlocked heap, it creates a new one. This is a simple
way to grow the number of subheaps as appropriate
without adding complicated schemes for hashing on
thread or processor ID, or maintaining workload sta-
tistics. However, there is no facility to shrink the sub-
heap list and nothing stops the heap list from growing
without bound. 

有一个用于堆(也称为区域)修剪的代码(heap_trim)。但它仅适用于完全空闲的区域。 - osgx
这种“简单的方式”增加子堆数量会导致连续创建竞技场(子堆)。由于堆碎片化,竞技场数量也可能增长。 - osgx

1

struct malloc_state(也称为mstate或arena描述符)的大小

glibc-2.2 (256+18)*4字节=约1 KB,适用于32位模式,而64位模式下则为约2 KB。 glibc-2.3 (256+256/32+11+NFASTBINS)*4字节=约1.1-1.2 KB,适用于32位模式,而64位模式下则为约2.4-2.5 KB。

请参阅glibc-x.x.x/malloc/malloc.c文件中的struct malloc_state。


1
你不需要将它舍入到下一个MMU分页块大小吗? 感谢您的回答! - rleir
这是内部竞技场描述符。每个竞技场描述符都放置在mmap-ed段中。最大mmaps限制为65k,已经硬编码。每个mmap从OS内核(VMA)中获取一些资源。 - osgx
所有竞技场描述符都在以main_arena为起点的循环链表中。每个新的竞技场都放置在mmap-ed区域的开头,偏移量为sizeof(heap_info) = 4xsizeof(void*) = 16或32字节。堆(mmaped段)是对齐的,并且大小从HEAP_MIN_SIZE到HEAP_MAX_SIZE。它具有mmap调用的本机对齐方式(=页面= 4k)。堆的其余部分(heap_info和mstate之后)用于malloc_chunks(malloced数据)。 - osgx
抱歉,HEAP_MIN_SIZE = 32 * 1024(32KB),HEAP_MAX_SIZE = 1024 * 1024(1MB)。 - osgx
HEAP_MAX_SIZE = 1MB 是ARENA的最大大小。因此,在大型程序中会有很多ARENA。 - osgx

0

来自malloc.c(glibc 2.3.5)第1546行

/*
  -------------------- Internal data structures --------------------
   All internal state is held in an instance of malloc_state defined
   below. 
 ...
   Beware of lots of tricks that minimize the total bookkeeping space
   requirements. **The result is a little over 1K bytes** (for 4byte
   pointers and size_t.)
*/

我得到了与32位模式相同的结果。 结果略大于1K字节


0
在我们的应用程序中,多个竞技场的主要成本是“黑暗”内存。这是由操作系统分配的内存,我们没有任何引用。您可以看到的模式是:
Thread X goes goes to alloc, hits a collision, creates a new arena.
Thread X makes some large allocations.
Thread X makes some small allocation(s).
Thread X stops allocating.

大块的分配被释放了。但是最后一个当前活动分配的高水位线上的整个区域仍在使用VMEM,除非其他线程在主要区域中遇到争用,否则它们不会使用这个区域。

基本上,这是“内存碎片化”的一个贡献者,因为有多个地方可以使用内存,但需要扩展一个区域并不是查找其他区域的理由。至少我认为这是原因,重点是你的应用程序可能会比你认为的占用更多的VM印记。如果你的交换空间有限,这主要会影响你,因为正如你所说,其中大部分会被分页出去。

我们(内存需求量大的)应用程序可以在这种方式下浪费10%以上的内存,并且在某些情况下可能会真正影响到我们。

我不确定为什么你想创建空的区域。如果分配和释放在同一个线程中,那么我认为随着时间的推移,你倾向于所有这些都在同一个线程特定的区域中而没有争用。你可能会在到达目的地时有一些小的波动,所以也许这是一个原因。


谢谢您。我想选择这个答案作为“最佳答案”,与 osgx 的回答并列。 - rleir

0

谢谢你的建议。注意:原问题并不涉及速度,我并不需要它变得更快。 - rleir
那里的高速是一个额外的奖励 :) - osgx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接