如果malloc/free对显然做了同样的工作,为什么C++标准库容器使用内存池

Why do C++ standard library containers use memory pools, if apparently the malloc/free pair does the same job?

本文关键字:标准 C++ 为什么 内存 工作 free malloc 如果      更新时间:2023-10-16

我读到对malloc/free的重复调用可能会很昂贵,因此C++标准库容器使用内存池,而不是在其析构函数中调用free。此外,我已经读到,这意味着C++标准库容器的性能可以高于手动分配和释放所有必要的C样式数组。

然而,我对此感到困惑,因为现在我正在阅读C常见问题解答:(http://c-faq.com/malloc/freetoOS.html)

malloc/free的大多数实现不会将释放的内存返回到操作系统,而只是使其可用于同一程序中的未来malloc调用。

这意味着malloc/free函数本质上试图做与C++标准库容器相同的工作:它们试图通过将内存保留在池中,然后根据请求提供该池中的程序片段来优化重复声明/回收内存。虽然我可以看到如果执行一次这样的优化的好处,但我的直觉告诉我,如果我们同时在几个不同的抽象层上开始这样做,性能实际上可能会降低,因为我们将重复相同的工作。

我误解了什么?

标准库的一些实现使用内存池。

通常,当您知道特定容器的内存需求时,您可能能够比不知道容器特定需求的通用内存管理器更好地管理其内存。

例如,如果您使用std::list<int>,则列表中的每个节点都具有相同的大小,并且让容器维护一个未使用节点的列表(仅分配两个指针以将节点添加到空闲列表或从空闲列表中删除节点)可能比将未使用的节点释放回new/delete(malloc/free)使用的更通用但更复杂的通用内存管理器更快。

称为malloc的通用内存管理实用程序通常针对常见情况进行优化。由于系统应该支持多个进程,每个进程的行为都不同,因此这种优化可能对某些应用程序很好,而对其他应用程序则不太好。通用分配器尝试考虑以下通用指南:

  • 最大化兼容性:分配器应该与其他分配器插头兼容;特别是它应该遵守ANSI/POSIX约定
  • 最大限度地提高可移植性:尽可能少地依赖于系统的功能(如系统调用),同时仍然为仅在某些系统上找到的其他有用功能提供可选支持;符合所有已知的系统对对齐和寻址规则的约束
  • 最小化空间:分配器不应该浪费空间:它应该从系统中获得尽可能少的内存,并且应该以最小化碎片的方式维护内存——程序不使用的连续内存块中的"洞">
  • 最小化时间:malloc()、free()和realloc例程在平均情况下应该尽可能快
  • 最大化可调性:可选功能和行为应由用户静态控制(通过#define等)或动态控制(通过mallopt等控制命令)
  • 最大化局部性:分配通常在一起使用的内存块。这有助于最大限度地减少程序执行期间的页面和缓存未命中
  • 最大化错误检测:通用分配器似乎不可能同时充当Purify之类的通用内存错误测试工具。但是,分配器应该提供一些方法来检测由于覆盖内存、多次释放等原因造成的损坏
  • 最小化异常

这个片段取自Doug Lea写的一篇伟大的文档,关于所谓的Doug Lea's malloc,这是多年来事实上的内存管理算法,我认为每个程序员都应该阅读这篇文章。

相反,当创建容器时,许多因素在编译时是已知的,甚至更多的因素可以在运行时预测,例如,我们知道要保存的对象的大小。利用这些知识,编写了标准容器,使其能够很好地与通用分配器配合使用。