如何实现内存堆

不确定如何标题,但问题是:

我听说过程序员在程序开始时分配大部分连续内存,然后在必要时将其处理掉。 这与每次需要内存时简单地访问操作系统形成对比。 我听说这会更快,因为它可以避免不断向操作系统询问连续的内存块的成本。

我相信JVM就是这样做的,维护自己的内存部分,然后从中分配对象。

我的问题是,如何实际实现这一点?

谢谢,dragonwrenn

大多数C和C ++编译器已经提供了堆内存管理器作为标准库的一部分,因此您根本不需要做任何事情以避免每次请求都触及操作系统。

如果你想提高性能,你可以简单地链接和使用一些改进的分配器。 例如Hoard ,现在删除的答案中提到了小麦(实际上非​​常好 – 小麦,为什么要删除它?)。

如果您想编写自己的堆管理器作为学习练习,以下是它需要执行的基本操作:

  • 从操作系统请求大块内存
  • 保留空闲块的链接列表
  • 当分配请求进入时:
    • 在列表中搜索一个足以满足所请求大小的块以及一些存储在一起的簿记变量。
    • 为当前请求拆分一个足够大的块,将其余部分放回到空闲列表中
    • 如果没有块足够大,请回到操作系统并要求另一个大块
  • 当释放请求进入时
    • 阅读标题以找出大小
    • 将新释放的块添加到空闲列表中
    • 可选地,查看紧接着的内存是否也列在空闲列表中,并将两个相邻的块组合成一个更大的块(称为合并堆)

您可以在程序开头分配一大块内存,以满足其需求。 然后你必须覆盖new和/或malloc,delete和/或free来从这个缓冲区返回内存。

在实现这种解决方案时,您需要编写自己的分配器(从块中获取源代码),并且最终可能会使用多个分配器,这通常是您首先分配内存池的原因。

默认内存分配器是一个很好的全面分配器,但不是所有分配需求的最佳。 例如,如果您知道要为特定大小分配大量对象,则可以定义一个分配器,该分配器分配固定大小的缓冲区并预先分配多个以获得一定的效率。

这是经典的分配器,也是非multithreading使用的最佳之一:

http://g.oswego.edu/dl/html/malloc.html

通过阅读其设计说明,您可以学到很多东西。

话虽如此,除非你的程序有非常不寻常的分配模式,否则编写自己的分配器或使用自定义分配器可能是一个非常糟糕的主意。 特别是如果您正在尝试替换系统malloc ,您可能会面临来自不同库(或标准库函数)的各种错误和兼容性问题,从而导致链接到“错误版本的malloc ”。

如果您发现自己只需要为几个特定任务进行专门分配,那么可以在不更换malloc情况下完成。 我建议为固定大小的对象查找GNU obstack和对象池。 这些涵盖了大多数专门分配可能具有实际实用性的案例。

  1. 是的,stdlib堆和OS堆/虚拟内存都非常麻烦。 操作系统调用非常慢,并且stdlib速度更快,但仍然有一些“不必要的”锁定和检查,并为分配的块增加了大量开销(即除了你分配的内容之外,还有一些内存用于管理)。
  2. 在许多情况下,通过使用静态结构可以完全避免动态分配。 例如,有时为unicode文件名定义64k静态缓冲区更好(更安全等),而不是定义指针​​/ std:string并动态分配它。
  3. 当程序必须分配大量相同结构的实例时,分配大内存块然后只是将实例存储在那里(顺序或使用自由节点的链接列表)要快得多 – C ++有一个“放置新的”那。
  4. 在许多情况下,当使用变量大小的对象时,可能的大小集合实际上非常有限(例如,像4 + 2 *(1..256)这样的东西),因此可以使用像[3]这样的几个池。无需收集垃圾,填补空白等
  5. 对于特定任务的自定义分配器来说,它比标准库中的一个更快,甚至比速度优化,但是过于通用的实现更快。
  6. 现代CPU /操作系统支持“大页面”,当您明确使用大块时,可以显着提高内存访问速度 – 请参阅http://7-max.com/

IBM developerWorks有一篇关于内存管理的好文章,有一个广泛的资源部分供进一步阅读: 内部内存管理 。

维基百科也有一些很好的信息: C动态内存分配 , 内存管理 。