【问题标题】:Is it possible to completely avoid heap fragmentation?是否可以完全避免堆碎片?
【发布时间】:2014-02-21 22:59:05
【问题描述】:

例如,如果动态内存的释放总是以与分配相反的方向进行。那样的话,能保证heap不会碎片化吗?

从理论的角度来看:对于非平凡的应用程序是否存在一些现实的方法来管理内存以完全避免堆碎片? (堆中每次原子变化后,堆是否仍然没有碎片化?)

【问题讨论】:

  • 对于您的第一种情况,标准当然不能保证。但是,如果分配模式如此简单,您可以轻松地自己制作一个高性能的。第二种情况显然是一个困难得多的问题。就我个人而言,我只是(成功地)在特定于应用程序的级别上完成了它。这并不是一件小事,因为它需要有足够的关于未来分配的信息才能知道将当前分配放在哪里。
  • 听起来您的分配满足堆栈规则,因此用作堆栈的大块连续内存应该没有碎片并满足您的分配需求。
  • @HansPassant 64 位(或者更确切地说是 48 位)地址空间非常好,但仍以页面粒度进行管理,即您仍然可以通过单个杂散分配阻止 4 KiB 的物理内存,无论多小.虚拟机空间并不是一切。这并不是说许多程序需要担心碎片化(它们不需要,而且不会比我们拥有商品 64 位的时间更长)。
  • 另一个可能相关的话题(出于对微优化的关注)是allocators and pre-allocated object pools的用法
  • @MartinJames:目前还不清楚该评论应该如何融入本次讨论。请详细说明。

标签: c++ memory-management


【解决方案1】:

对于非平凡的应用程序如何管理内存以完全避免堆碎片存在一些现实的方法

是的。分配堆栈上的所有内容。这似乎是一个完美的建议,但我已经在不平凡的程序中做到了。

编辑为了绝对清楚和避免疑问,我说'堆栈'。我显然是指自动存储,即局部变量堆栈,而不是任何堆栈类。一些关于这个的cmets基本上是钝的。

【讨论】:

  • @MartinJames:我认为没有人怀疑他在谈论使用具有自动生命周期的变量。大多数实现将它们放在调用堆栈上,由专门指定为堆栈边缘指针的 CPU 寄存器管理。
  • 赞成只是为了让huevos建议这种方法。虽然我认为全局对象/数组也可以在这里提供帮助。许多老式游戏在大型全球阵列中处理所有事情。它很丑,但它有效并且零内存分配器开销。
  • 我希望看到有人编写一个不受严格限制的综合编译器(例如 Pascal、C 或 C++ 类型的语言)(应对大型源代码和大型函数,在代码生成)使用此解决方案...
  • @EJP:太棒了,那太好了。但是你是如何通过堆栈这种类型的操作来解决的:用户打开文档 A,然后是文档 B,然后关闭文档 A?
  • @user3123061 交换后弹出。
【解决方案2】:

真正避免堆碎片的唯一方法是自己进行内存管理,跟踪您使用的所有指针,并不时对堆进行碎片整理。但请注意,没有这样做是有原因的:它没有回报。对堆进行碎片整理所需的代码和性能工作量太大了。

只是一些轶事的历史信息:
旧的 MacOS(MacOS-X 之前的时代!)使用所谓的内存对象句柄:指向指向实际内存区域的指针列表的指针。这样做的好处是操作系统可以通过修改表中的指针来移动内存对象;句柄的任何引用都将保持不变。但是每次你想通过系统调用访问它的数据时,你必须锁定一个句柄。需要指出的是,这是在多核成为主流之前,所以并没有真正的并发。对于多线程应用程序,每次都必须锁定句柄,至少在允许其他线程调用系统的情况下。我再说一遍,句柄在 MacOS-X 中无法生存是有原因的......

【讨论】:

  • 当然,Mac 程序员要么忘记解锁他们的手柄,要么故意在“早期”调用中锁定手柄,然后才释放它,从本质上规避移动......是的,这不是一个很棒的系统...
  • @MatsPetersson:事实上,考虑到它的设计有多糟糕,编写真正有效的代码的几乎唯一方法就是大部分时间都锁定大部分内存。大问题:lock 和 unlock 调用不平衡,所以如果你锁定相同的内存(比如说)三次,one 调用 unlock 仍然会解锁它。 16 位 Windows 使用了相同的 basic 理念,但更加理智(例如,lock/unlock 调用 did balance)。
  • 我写了很多经典的 Mac OS 代码。你不必一直HLock 一切。操作系统不会随心所欲地重新洗牌——只有当你真正调用操作系统函数时,它才能做一个紧凑的事情。如果您编写了一个根本不调用操作系统的紧密循环,则可以保证您的句柄保持不变。
  • 它仍然是处理程序中一些大内存块的好方法。我在一个拥有 300 个平均每个 64 KB 的文档的情况下使用它。不幸的是,平均值具有误导性,所以我不得不实现自己的压缩算法。
  • 其实Win16也有used the handle approach,虚拟内存一出现就放弃了。
【解决方案3】:

我认为理论上可以实现这一点(假设堆实现也在做“正确的事情”[例如,在释放块时立即合并块])

但在任何解决一些实际问题的实际应用中,这不太可能。当然,std::stringstd::vector 的任何使用几乎肯定会以“无序方式”分配/释放内存。

如果您遇到堆碎片是一个潜在问题的场景,那么使用减少/消除这种问题的解决方案几乎肯定会更好(例如,固定大小的存储桶分配,用于各种类型分配的单独堆 - 这些只是许多中的两个不同的解决方案)。

【讨论】:

    【解决方案4】:

    可以将堆划分为允许分配特定大小的内存的区域。这样你的堆就不会被分区,但你会有内存消耗开销和耗尽空闲块的风险,但有时它是有充分理由的(比如当软件必须 24/7/365 保持在线时。大小可以是权力2,或者是应用程序最常用的大小,或者只是在第一次分配大小 N 时按需分配的新内存区域,直到某个合理的 N。

    看起来与此类似(* - 已分配块, - 空闲块)

     size=1: [*][ ][*][*][*][ ][ ][ ][ ][ ]
     size=2: [  ][  ][  ][**][**][  ][  ][  ]
     size=4: [    ][****][    ][    ][    ]
     size=8: [        ][        ][        ]
     ....
    

    我不久前在试验这个主题时写的一个可视化工具:http://bobah.net/d4d/tools/cpp-heapmap

    【讨论】:

    • 是的,主要操作系统都会这样做,例如 Windows“低碎片化堆”。但是,自然不会将块大小降至 1,因为元数据开销是可怕的。
    • 另一个例子是在 Linux 中。 slab 分配器维护每种对象的区域,例如目录的 dentry,以及一些用于 16 字节或 32 字节分配的通用区域,kmalloc-16,kmalloc-32。
    【解决方案5】:

    在 Java 等托管语言中,堆碎片整理一直在发生。这是可能的,因为 Java“指针”实际上是引用,并且 Java 知道它们并且能够在移动堆中的对象时移动它们。

    在 C 和 C++ 中,指针可以是任何东西,它们可以被计算、组合、更新等。这意味着一旦分配了某个东西,它就不能移动,因为你永远不知道实际上什么可能是指向它的指针 - 并且立即在一般情况下无法进行碎片整理。

    减少碎片的唯一方法是仅将相同大小的元素存储在相同的内存区域中,但这太不灵活了,在许多现实生活中的编程情况下是不切实际的。

    【讨论】:

      【解决方案6】:

      正如上面 delnan 所提到的,另一个问题是虚拟内存页面碎片,当有大量分配和释放内存时可能会发生这种情况。 Windows .net 应用程序依赖 .net 的垃圾收集方案来重新打包分配的内存,但这会导致正在运行的程序暂停。我不确定每次停顿多长时间。

      【讨论】:

        【解决方案7】:

        我的大部分软件设计和编程都是在硬实时系统中完成的。这些是控制炼油厂、发电厂、钢厂、铜冶炼厂、石油化工厂、石油和天然气管道储存和运输设施的非常关键的实时系统。我们不能允许任何会导致功能丧失或重新启动任何控制服务器的内存碎片,因为结果将是最小的经济损失,最严重的灾难性损坏和生命损失。

        我们简单地创建了三个固定的缓冲区大小:小型、中型和大型,并且在启动时,我们将所有内存预分配到这 3 个确切的大小,并通过一个简单的链表自己管理内存。从来不需要垃圾收集,但我们确实需要显式分配和释放缓冲区。

        【讨论】:

        • 现在只是偶然发现了这个。这对我来说很有趣。有没有什么地方可以找一个简单的例子来说明这样的事情。或者,如果它不是专有的,你能详细说明一下吗?
        • 您知道典型的 malloc 实现完全按照您刚才所说的那样实现:) 它使用链表来分配和释放内存。
        猜你喜欢
        • 2010-09-14
        • 2014-06-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-28
        • 2019-11-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多