为啥 malloc() 基于链表?

Posted

技术标签:

【中文标题】为啥 malloc() 基于链表?【英文标题】:Why is malloc() based on linked-list?为什么 malloc() 基于链表? 【发布时间】:2015-06-29 07:26:55 【问题描述】:

在最坏的情况下,在大小为n 的内存部分(这是正确的术语吗?)上,链表需要O(n) 时间来分配合适大小的内存块。

但是,如果malloc 是基于树的,例如区间树,则只需要O(logn) 时间。此外,一棵树无需额外的时间(就时间复杂度而言)就可以满足这些要求,例如"Find the smallest block of free memory whose size is larger themx""Always allocate on the borders of free memory""Free only a part of the allocated memory"。一个缺点可能是释放内存需要O(logn) 时间。

谢谢

ps。看到Data structures for traversable memory pool的问题了,但是作者好像没搞明白。

【问题讨论】:

是什么让您认为malloc 使用了链表?仅仅因为 一个 (许多)实现使用了它? @JoachimPileborg 我读到ptmalloc2 在链表中使用块,它在最新版本的 glibc 中。 您是否真的尝试过编写自己的 mallocfree 函数,使用其他东西,并测量与当前实现相比分配所需的时间。 @MatsPetersson 我写了一个模型,在数组上玩,假装一些“程序”需要内存。 结果如何?整体执行时间是否有很大改善?在琐碎的程序中,我看到malloc 占用了很大一部分时间。但在非平凡的程序中,通常不是花费时间的地方——显然高度依赖于程序的确切功能!但是内存分配至少在过去 40 年里就已经存在了,而且模型在很大程度上是相同的——我相信有些人已经研究过它。 【参考方案1】:

我不知道答案,但这里有一些想法:

绝对没有要求malloc 以特定方式实现。但是,在最坏的情况下,不平衡树与链表一样糟糕。平衡链表需要更多维护。一棵树,每个节点有两个链接,也比单链表占用更多的内存。删除链表中的节点更容易,在末尾插入也很容易。

在大多数系统中,每个malloc (几乎)恰好有一个free - 所以如果你通过让另一个变慢来让一个更快,你获得的收益很少。

“下一个分配与上一个相同”也比较常见,这意味着如果最后一个分配在列表中是第一个,这是一个 O(1) 操作。

在实时系统中,桶通常用于分配,因此有许多固定大小,每次从主堆中分配一些东西时,大小都会四舍五入到最接近的较大大小,当释放它进入那个大小的桶(这是一个链表)。如果已经有该大小的空闲元素,则使用该分配。除了分配/释放的速度为 O(1) 之外,这还具有减少碎片的好处——“将所有堆撕成小块,然后不留下任何大块”并非完全不可能,但至少不是每次分配一个字节,直到你在一次分配中获得一半的堆大小,可能会占用大部分内存。

(另外,在 Linux 的 GLIBC 中,超过一定大小的分配根本不会在链表中结束 - 它们直接通过 mmap 分配,并在调用 free 时以 munmap 释放)

最后,算法复杂度并不是一切——在现实生活中,它是实际花费在重要事情上的时间——即使算法有 O(n) 但每个操作都很快,它也可以击败 O(logn)。同样,特别是在 C++ 中,小分配占主导地位,这意味着每个节点的更多内存开销是一个重要因素。

【讨论】:

正因为每个malloc 都有一个free,所以基于树的malloc 使用O(logn)+O(logn)=O(logn) 时间和链表O(n)+O(1)=O(n) 而且分配平均为 O(n),而不是 O(1),即使使用存储桶也是如此。 是的,但您还必须考虑实际时间。 Buckets 保证 O(1) 的分配和免费 - 因为桶中的项目都是相同大小的,你只需选择最近释放的项目 - 或者挖出一点“新鲜”如果空闲列表中没有任何内容,则堆。但我确信使用效率较低的存储桶来实现某些东西是可能的——总是可以做到的。 你的意思是时间复杂度中的常数可能很大吗?【参考方案2】:

没有说明 malloc 需要基于链表的规范。在平台之间,实现可能会发生变化。在一个平台上,速度可能至关重要,可以实现一棵树,在另一个平台上,内存更昂贵,并且使用链表(或此类)以尽可能节省内存。

【讨论】:

以上是关于为啥 malloc() 基于链表?的主要内容,如果未能解决你的问题,请参考以下文章

静态链表 初始化 定位 Malloc Free 插入 删除

数据结构-链表的操作

为啥不使用指针数组来优化链表而使用跳过列表呢?

链表操作

c语言中创建动态链表

在一台内存为2G的机器上,malloc(20G) 会怎样?如果是new(20G) 会怎样?