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

标签 c++ c memory-management malloc free

我想知道 mallocfree 是如何工作的。

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;
}

如果可能的话,如果答案能深入内存,我将不胜感激。

最佳答案

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

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

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

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

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

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

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

为什么你的代码会崩溃:

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

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

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

关于c++ - malloc() 和 free() 是如何工作的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1119134/

相关文章:

c++ - 如果一个函数对象有很多参数,它是什么样子的?

c++ - 删除单例对象时内存泄漏,cpp

c - 循环遍历 C 预处理器中的 header

c - 如何检查特定字符串是否为字符值的数值?

c - 如何检测点是否位于线段

memory-management - 物理寻址和虚拟寻址概念之间的区别

.net - C# 如何管理循环内由新运算符分配的内存?

c++ - 套接字服务器无法接收客户端发送的消息

c++ - 在 C++ 中没有比较器函数的情况下,按对中的第一个元素按升序对对 vector 进行 stable_sort

c++ - 删除 visual studio 标题中的 void 指针