小编典典

malloc() 和 free() 是如何工作的?

all

我想知道如何malloc工作free

int main() {
    unsigned char *p = (unsigned char*)malloc(4*sizeof(unsigned char));
    memset(p,0,4);
    strcpy((char*)p,"abcdabcd"); // **deliberately storing 8bytes**
    cout << p;
    free(p); // Obvious Crash, but I need how it works and why crash.
    cout << p;
    return 0;
}

如果可能的话,如果答案在记忆水平上是深入的,我将不胜感激。


阅读 106

收藏
2022-04-21

共1个答案

小编典典

好的,一些关于 malloc 的答案已经发布。

更有趣的部分是 free 是如何工作 的(在这个方向上,malloc 也可以更好地理解)。

在许多 malloc/free 实现中,free
通常不会将内存返回给操作系统(或至少在极少数情况下)。原因是您的堆中会出现间隙,因此可能会发生,您只需用间隙完成 2 或 4 GB
的虚拟内存。这应该避免,因为一旦虚拟内存完成,你就会遇到很大的麻烦。另一个原因是,操作系统只能处理具有特定大小和对齐方式的内存块。具体来说:通常操作系统只能处理虚拟内存管理器可以处理的块(通常是
512 字节的倍数,例如 4KB)。

所以将 40 字节返回给操作系统是行不通的。那么免费有什么用呢?

Free
会将内存块放入自己的空闲块列表中。通常它还会尝试将地址空间中的相邻块融合在一起。空闲块列表只是一个内存块的循环列表,开头有一些管理数据。这也是为什么使用标准
malloc/free 管理非常小的内存元素效率不高的原因。每个内存块都需要额外的数据,并且尺寸越小,碎片就越多。

空闲列表也是 malloc
在需要新的内存块时首先查看的位置。它在从操作系统调用新内存之前被扫描。当发现一个大于所需内存的块时,它被分为两部分。一个返回给调用者,另一个被放回空闲列表。

此标准行为有许多不同的优化(例如,对于小块内存)。但是由于 malloc 和 free
必须如此通用,所以当替代品不可用时,标准行为始终是后备。在处理空闲列表方面也有优化——例如将块存储在按大小排序的列表中。但是所有的优化也有其自身的局限性。

为什么你的代码会崩溃:

原因是通过将 9 个字符(不要忘记尾随的空字节)写入一个大小为 4
个字符的区域,您可能会覆盖为位于您的数据块“后面”的另一块内存存储的管理数据(因为这些数据通常存储在内存块的“前面”)。当 free
然后尝试将您的块放入空闲列表时,它可能会触及此管理数据并因此绊倒一个被覆盖的指针。这会使系统崩溃。

这是一种相当优雅的行为。我还看到过某个地方的失控指针覆盖了内存空闲列表中的数据并且系统没有立即崩溃但后来出现一些子例程的情况。即使在中等复杂度的系统中,此类问题也可能非常非常难以调试!在我参与的一个案例中,我们(一大群开发人员)花了几天时间才找到崩溃的原因——因为它位于与内存转储指示的位置完全不同的位置。这就像一颗定时炸弹。你知道,你的下一个“free”或“malloc”会崩溃,但你不知道为什么!

这些是一些最严重的 C/C++ 问题,也是指针如此成问题的原因之一。

2022-04-21