[英]Why does `realloc` not re-allocate in-place when possible?
来自 c99:
realloc
函数释放ptr
指向的旧对象,并返回一个指向大小由 size 指定的新对象的指针。 新对象的内容应与释放前的旧对象的内容相同,直到新旧大小中较小的一个。 新对象中超出旧对象大小的任何字节都具有不确定的值。[..]
realloc 函数返回一个指向新对象的指针(它可能与指向旧对象的指针具有相同的值),如果无法分配新对象,则返回空指针。
我很惊讶标准没有指定realloc
应该“尝试”进行就地重新分配。 通常情况下,如果重新分配的大小比当前分配的大小时,我本来期望的标准,以确保realloc
将返回相同的指针。
如果尺寸减小,标准是否有逻辑不指定realloc
应该就位?
我想我会提出这个答案。
“尝试”的概念在标准的上下文中没有多大意义 - 实施者在尝试足够多之前必须尝试多少? 如何衡量合规性?
许多常见的实现将完全按照您的建议工作:如果您向下调整大小,甚至向上调整大小并且以下内存恰好是空闲的,则它们可能会在调整内务后返回原始指针但不必复制任何数据。 好极了!
但是我可以想到很多原因,即使可能,分配器也不会这样做:
一些分配器为不同的大小保留不同的 arena,其中(组成)它是一个不同的池,用于 1-128 字节的块与 64kbytes 和更大的块。 如果“大”池必须保留小分配,整个方案就会崩溃。 如果您有意在页面边界上保留“大”分配,则尤其如此。
多线程感知应用程序通常必须特别注意避免争用,以便内存分配不会成为瓶颈。 如果您正在重新分配在不同线程中分配的块,则为您提供一个新块(带有副本)并推迟释放旧指针可能是非阻塞的,但允许您保留相同的指针会阻止这个或其他一些线程。
调试分配器会有意返回不同的指针,以确保程序不会错误地挂在旧指针上:这迟早会破坏事情。
我想不出标准中的“请尝试”语句会改变任何库设计者的任何决定的情况。 如果对于给定的实现保持相同的指针有意义,那么他们当然会使用它,但如果有一个压倒一切的技术原因不这样做,那么他们就不会。
我也不确定我能想到这种轻推会对图书馆用户产生任何影响的情况。 您仍然必须对其进行编码以解决所有情况,即使是“尝试”的情况,因此它不会为您节省任何代码。
最后,这是一个标准永远不会束缚实施者的实现细节,库将根据其自身的优点(性能、代码大小、可靠性等)来判断,这只是一个方面。
如果出于某种原因确实需要这种行为,则始终可以编写自己的分配器。
编辑:即使重新分配相同的大小,分配器也希望返回不同的指针的另一个原因:减少内存碎片。
如果我的 realloc 请求在任何一侧都有大量可用空间的时候到来,分配器可以意识到:我可以将这个块扩展到位(快速而简单),或者我可以将它移到其他地方并合并剩下的后面变成一个更大的空闲块。
对于客户编写的项目来说,这一直是一个烦人的问题:很久以前用 32 位 Delphi 编写的,它在内存压力很大的情况下一次运行数天,最终内存非常碎片化,无法为适度的请求提供服务即使有数百兆字节免费。
Ref: 连接的 Delphi 字符串是否保存在保留对字符串的引用的隐藏临时变量中?
在 Delphi 中我对此无能为力,但在 C 中很容易想象“积极避免内存碎片”是分配器的一个属性。
一个标准是一个通用的描述,它不需要指定它是否尝试做某事,它描述了一个函数的一般行为。
出于优化目的,系统将尝试在同一位置调整缓冲区大小,这很清楚且符合逻辑,但由于无法保证会发生这种情况,因此无法在标准中指定它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.