Memory management 使用什么数据结构来实现动态内存分配堆?

Memory management 使用什么数据结构来实现动态内存分配堆?,memory-management,data-structures,heap,Memory Management,Data Structures,Heap,我一直认为a是用来实现a的,但有人告诉我我错了 堆(例如,由典型的malloc例程或Windows的HeapCreate等实现的堆)通常是如何实现的?他们使用什么数据结构 我没有问的是: 在网上搜索时,我看到了大量关于如何在严格限制下实现堆的描述。 举几个例子,我看到了很多关于如何实现的描述: 永远不会将内存释放回操作系统的堆(!) 仅在较小、大小相似的块上提供合理性能的堆 仅为大型连续块提供合理性能的堆 等等 有趣的是,他们都回避了更难的问题: 如何实现“正常”的通用堆(如malloc,H

我一直认为a是用来实现a的,但有人告诉我我错了

堆(例如,由典型的
malloc
例程或Windows的
HeapCreate
等实现的堆)通常是如何实现的?他们使用什么数据结构

我没有问的是: 在网上搜索时,我看到了大量关于如何在严格限制下实现堆的描述。
举几个例子,我看到了很多关于如何实现的描述:

  • 永远不会将内存释放回操作系统的堆(!)
  • 仅在较小、大小相似的块上提供合理性能的堆
  • 仅为大型连续块提供合理性能的堆
  • 等等
有趣的是,他们都回避了更难的问题:
如何实现“正常”的通用堆(如
malloc
HeapCreate
后面的堆)


他们使用什么样的数据结构(可能还有算法?

注意:下面的答案假设您使用的是一个典型的、带有虚拟内存的现代系统。C和C++标准不需要虚拟内存;因此,如果没有此功能,您当然不能在硬件上依赖此类假设(例如,GPU通常没有此功能;PIC等非常小的硬件也没有此功能)


这取决于您使用的平台。堆可以是非常复杂的野兽;它们不仅使用单一的数据结构;而且没有“标准”的数据结构。即使堆代码所在的位置也因平台而异。例如,堆代码通常由Unix机器上的C运行时提供;但通常由Windows上的操作系统提供

  • 是的,这在Unix机器上很常见;由于*nix的底层API和内存模型的运行方式。基本上,向这些系统上的操作系统返回内存的标准API只允许在分配用户内存的“边界”和用户内存与系统设施(如堆栈)之间的“漏洞”上返回内存。(有关的空气污染指数为)。许多堆不是将内存返回操作系统,而是尝试重用程序本身不再使用的内存,而不是将内存返回系统。这在Windows上不太常见,因为它的等价物
    sbrk
    VirtualAlloc
    )没有此限制。(但与
    sbrk
    类似,它非常昂贵,并且有一些警告,比如只分配页面大小和页面对齐的块。因此堆尝试尽可能少地调用这两个块)
  • 这听起来像是一个“块分配器”,它将内存分成固定大小的块,然后只返回一个空闲块。据我(尽管有限)了解,Windows的RtlHeap为不同的已知块大小维护了许多类似的数据结构。(例如,它将有一个用于大小为16的块)RtlHeap调用这些“lookaside list”
  • 我真的不知道有哪种结构能很好地处理这个案子。对于大多数分配系统来说,大数据块是有问题的,因为它们会导致地址空间的碎片化

  • 我发现讨论主要平台上使用的常见分配策略的最佳参考是这本书。第4章的所有内容都专门讨论堆数据结构(以及当用户不正确地使用所述堆系统时引起的问题)。

    分配器往往非常复杂,并且在实现方式上往往存在显著差异

    您不能用一种通用的数据结构或算法来描述它们,但有一些通用的主题:

  • 内存是以大块的形式从系统中获取的,通常一次获取兆字节
  • 然后,在执行分配时,这些块会被拆分为多个较小的块。与分配的大小不完全相同,但通常在某些范围内(200-250字节、251-500字节等)。有时这是多层的,在这里你会有一层额外的“中间块”,在你的实际请求之前
  • 控制要从哪个“大块”中断开一块是一件非常困难和重要的事情——这会极大地影响内存碎片
  • 为每个范围维护一个或多个空闲池(也称为“空闲列表”、“内存池”、“查找列表”)。有时甚至线程本地池。这可以大大加快分配/取消分配许多大小相似的对象的模式
  • 大型分配的处理方式有点不同,这样就不会浪费大量RAM,也不会进行太多池化(如果有的话)
  • 若您想查看一些源代码,它是一个现代的高性能分配器,并且应该在其他常见分配器的复杂性方面具有代表性。是另一个常见的通用分配器,他们的网站详细介绍了所有血淋淋的实现细节。英特尔有一个专为高并发性而构建的分配器

    Windows和*nix之间有一个有趣的区别。在*nix中,分配器对应用程序使用的地址空间具有非常低级的控制。在Windows中,您基本上有一个过程粒度、速度慢的分配器
    VirtualAlloc
    ,以使您自己的分配器脱离它

    这导致*nix兼容的分配器通常直接为您提供
    malloc
    /
    免费
    实现,其中假定您对所有内容都只使用一个分配器(否则它们会相互践踏),而Windows特定的分配器提供附加功能,将
    malloc
    /
    单独保留为自由的
    ,并且可以协调使用(例如,您可以使用HeapCreate创建可以与其他堆一起工作的私有堆)

    在实践中,这种灵活性的交换使*nix分配器在性能方面有了一个小的提升。很少看到一个应用程序故意在Windows上使用多个堆——大部分是由acciden开发的