代码之家  ›  专栏  ›  技术社区  ›  Thomas Koschel

如何避免堆碎片?

  •  13
  • Thomas Koschel  · 技术社区  · 17 年前

    我目前正在从事一个医学图像处理项目,它需要大量的内存。我能做些什么来避免堆碎片并加快对已经加载到内存中的图像数据的访问?

    该应用程序对图像数据进行一些预处理,如重新格式化、计算查找表、提取感兴趣的子图像。。。该应用程序在处理期间需要约2GB RAM,其中约1,5GB可用于图像数据。

    9 回复  |  直到 5 年前
        1
  •  17
  •   Jeff Kotula    17 年前

    如果您正在进行医学图像处理,则很可能一次分配大的块(512x512,每像素2字节图像)。如果你分配较小的对象,碎片会咬你一口 之间

    对于这个特定的用例,编写自定义分配器并不一定很难。可以为图像对象使用标准C++分配器,但对于像素缓冲区,可以使用所有在图像对象内进行管理的自定义分配。下面是一个快速而肮脏的大纲:

    • 使用结构的静态数组,每个结构都有:
      • 一个可以容纳N个图像的坚实内存块——分块将有助于控制碎片——尝试初始的N个5左右
      • 一个并行布尔数组,指示相应的图像是否正在使用
    • 要分配,请在数组中搜索空缓冲区并设置其标志
      • 如果未找到,请在数组末尾追加一个新结构
    • 要取消分配,请在数组中找到相应的缓冲区并清除布尔标志

    这只是一个简单的想法,有很大的变化空间。主要技巧是避免释放和重新分配图像像素缓冲区。

        2
  •  5
  •   CB Bailey    17 年前

    答案是有的,但如果不知道问题的细节,就很难笼统。

    我假设是32位WindowsXP。

    尽量避免需要100MB的连续内存,如果你运气不好,一些随机DLL会通过你的可用地址空间在不方便的地方自动加载,从而迅速减少非常大的连续内存面积。根据您需要的API,这可能很难防止。除了一些“正常”内存使用之外,仅仅分配几个400MB内存块就可以让您无处分配最后一个“小”40MB内存块,这可能非常令人惊讶。

    另一方面,一次预先分配合理大小的块。10MB左右是一个很好的折衷块大小。如果您能够将数据划分成这种大小的块,那么您将能够合理有效地填充地址空间。

    如果地址空间仍然不足,则需要能够基于某种缓存算法对块进行分页进出。选择正确的块进行分页在很大程度上取决于处理算法,需要仔细分析。

    另一个决定是选择把事情分到哪里。您可能决定只将它们写入临时文件。您还可以研究Microsoft的地址窗口扩展API。在这两种情况下,您都需要在应用程序设计中小心地清理任何指向即将被分页的内容的指针,否则会发生非常糟糕的事情(tm)。

    祝你好运

        3
  •  5
  •   Uri    17 年前

    这不会减少内存使用,但可能会对页面交换和性能产生巨大影响。

        4
  •  2
  •   Torlack    17 年前

    如果没有关于这个问题的更多信息(例如语言),您可以做的一件事就是通过重用分配而不是分配、操作和释放来避免分配混乱。分配器,例如 dlmalloc 比Win32堆更好地处理碎片。

        5
  •  2
  •   Suma    17 年前

    32b应用程序很难实现1.5-2GB。最优雅的方法是使用64b操作系统和64b应用程序。即使使用64b操作系统和32b应用程序,只要您使用 LARGE_ADDRESS_AWARE .

    但是,由于需要存储图像数据,您也可以使用 File Mapping as a memory store -这可以通过这样一种方式来实现,即您有一个提交和可访问的内存,但根本不使用任何虚拟地址。

        6
  •  1
  •   Jorge Ferreira    17 年前

    我猜你是说 避免碎片化 避免碎片整理 . 还猜测您正在使用非托管语言(C或C++)。我建议您分配大块内存,然后从分配的内存块中进行堆分配。这个内存池因为包含大块内存而不太容易出现碎片。总之,您应该实现一个自定义内存分配器。

    here .

        7
  •  1
  •   Kasprzol    17 年前

    我猜想您正在使用非托管的东西,因为在托管平台中,系统(垃圾收集器)负责碎片。

    此外,您还可以创建自己的数据存储。例如,在我目前正在工作的项目中,我们有一个位图的自定义存储(池)(我们将它们存储在大量连续的大块内存中),因为我们有很多位图,我们跟踪堆碎片,并在碎片太大时对其进行碎片整理。

        8
  •  1
  •   florin    17 年前

    您可能需要实现手动内存管理。图像数据是否长寿?如果没有,那么您可以使用ApacheWeb服务器使用的模式:分配大量内存并将它们包装到内存池中。将这些池作为函数中的最后一个参数传递,以便它们可以使用该池来满足分配临时内存的需要。一旦调用链完成,池中的所有内存都将不再使用,因此您可以擦洗内存区域并再次使用它。分配速度很快,因为它们只意味着向指针添加一个值。释放速度非常快,因为您可以一次释放非常大的内存块。

    如果应用程序是多线程的,则可能需要将池存储在线程本地存储中,以避免跨线程通信开销。

        9
  •  0
  •   Frederik Slijkerman    16 年前

    这是一个简单的解决方案,不需要使用自定义内存管理器。