使用结构池正确处理内存

时间:2014-07-21 09:32:15

标签: c memory memory-management pool

我有一个包含三个结构池的程序。对于它们中的每一个,我使用一个使用过的结构列表,另一个用于未使用的结构。在执行期间,程序使用结构,并根据需要将它们返回池中。此外,还有一个垃圾收集器来清理“僵尸”结构并将它们返回池中。

在执行开始时,虚拟内存按预期显示分配的内存大约为10GB *,当程序使用该池时,RSS内存会增加。

虽然使用的节点回到池中,标记为未使用的节点,但RSS内存不会减少。我期待这一点,因为操作系统不知道我正在对内存做什么,无法注意到我是在真正使用它们还是管理池。

我想要做的是强制未使用的内存随时返回虚拟内存,例如,当RSS内存增加到X GB以上时。

在给定内存指针的情况下,是否有任何方法可以将内存区域标记为虚拟内存?我知道这是操作系统的责任,但也许有办法强迫它。

也许我不应该关心这个,你觉得怎么样?

提前致谢。

  • 注1:此程序用于高性能计算,这就是它使用这么多内存的原因。

我提供了几个文件的池使用情况与内存使用情况的图片。正如您所看到的,池使用的突然下降是由于垃圾收集器,我希望看到的是,这种下降反映在内存使用中。

Struct Pools Usage & Memory Usage

2 个答案:

答案 0 :(得分:3)

只要您通过mmap而不是malloc分配内存,就可以执行此操作。您希望将madvise函数与POSIX_MADV_DONTNEED参数一起使用。

请记住在再次使用它们之前使用POSIX_MADV_WILLNEED进行madvise以确保它们背后有实际内存。

这实际上并不能保证页面会被换出,但会在有时间的情况下给内核提供强有力的提示。

答案 1 :(得分:0)

Git 2.19(2018年第三季度)提供了一个使用mmap而非malloc的struct内存池示例。

对于一棵大树,索引需要保存在堆上分配的许多缓存条目。
现在,这些缓存条目已从专用内存池中分配出来,以分摊malloc(3)的开销。

请参见commit 8616a2dcommit 8e72d67commit 0e58301commit 158dfefcommit 8fb8e3fcommit a849735commit 825ed4d,{{3} }(2018年7月2日)通过commit 768d796(由Jameson Miller (jamill)Junio C Hamano -- gitster --中合并,2018年8月2日)

  

block alloc:从mem_pool中分配缓存条目

     

从磁盘读取大索引时,一部分时间由malloc()调用控制。
  可以通过分配一个大的内存块并通过内存池自己管理它来缓解这种情况。

     

此更改将缓存条目分配移动到内存池的顶部。

     

设计:

     

index_state结构将获得一个关联的memory_pool的概念,将从中分配cache_entries。
  从磁盘读取索引时,我们获得有关条目数量及其大小的信息,这些信息可以指导我们确定初始内存分配的大小。
  丢弃索引时,关联的memory_pool也将被丢弃-因此cache_entry的生存期与为其分配的index_state的生存期相关。

     

对于拆分索引,请遵循以下规则。
  首先,定义一些术语:

     

术语:

     
      
  • 'the_index':表示索引的逻辑视图
  •   
  • 'split_index':代表“基本”缓存条目。   从拆分索引文件中读取。
  •   
     

'the_index'可以引用单个split_index,也可以引用cache_entries中的split_indexthe_index将在split_index之前被丢弃。
  这意味着当我们在存在拆分索引的情况下分配cache_entries时,我们需要从split_index的内存池中分配条目。

     

这使我们能够遵循the_index可以从split_index引用cache_entries的模式,并且在cache_entries仍被引用时不会被释放。

     

管理瞬态cache_entry结构:

     

通常为索引分配高速缓存条目,但是并非总是如此。有时会分配缓存条目,因为这是现有checkout_entry函数所使用的类型。
  因此,现有代码需要处理与索引/内存池相关联的缓存条目,以及仅临时存在的缓存条目。
  关于如何处理此问题,已考虑了几种策略。

     

选择的方法:

     

cache_entry类型中添加了一个额外的字段,以跟踪是否从内存池分配了cache_entry。
  当前这是一个int字段,因为现有的位不再可用   ce_flags位字段。
  如果/需要更多位,则可以将该新字段转换为合适的位字段。

     

我们决定对已知的内存池区域进行跟踪和迭代是   比添加一个额外的字段来跟踪此状态更不理想。