所以我在堆中分配了 256 个块:
char* ptr1 = malloc(128);
char* ptr2 = malloc(128);
现在,在我释放 ptr2(我假设当前位于堆顶部)之后,程序中断(堆的当前位置)不会减少。但是,如果我执行另一个 malloc,则 malloc 返回的地址与释放的地址相同。
所以我有以下问题:
当我释放一个块时,为什么程序中断没有减少?当我调用 free 时到底发生了什么?它如何跟踪释放的内存以便下次我声明 malloc 时地址是相同的?
所以我在堆中分配了 256 个块:
char* ptr1 = malloc(128);
char* ptr2 = malloc(128);
现在,在我释放 ptr2(我假设当前位于堆顶部)之后,程序中断(堆的当前位置)不会减少。但是,如果我执行另一个 malloc,则 malloc 返回的地址与释放的地址相同。
所以我有以下问题:
当我释放一个块时,为什么程序中断没有减少?当我调用 free 时到底发生了什么?它如何跟踪释放的内存以便下次我声明 malloc 时地址是相同的?
这是未指定的行为。您不能依赖任何单一的答案,除非您只关心一个特定的平台/操作系统/编译器/libc 组合。您没有指定操作系统,C 标准没有描述或要求任何特定的实现。从 C99 开始(我还没有 C11 的最终发布版本):
7.20.3
连续调用 calloc、malloc 和 realloc 函数分配的存储顺序和连续性是未指定的。如果分配成功,则返回的指针经过适当对齐,以便可以将其分配给指向任何类型对象的指针,然后用于访问已分配空间中的此类对象或此类对象的数组(直到空间被显式释放) . 已分配对象的生命周期从分配一直延伸到解除分配。每个这样的分配都应产生一个指向与任何其他对象不相交的对象的指针。返回的指针指向分配空间的开始(最低字节地址)。如果无法分配空间,则返回空指针。如果请求的空间大小为零,则行为是实现定义的:要么返回空指针,要么
这本 GNU libc手册可能会有所帮助。
这是要点
有时,free 实际上可以将内存返回给操作系统,并使进程更小。通常,它所能做的就是允许稍后调用 malloc 来重用空间。同时,该空间作为 malloc 内部使用的空闲列表的一部分保留在您的程序中。
当我释放一个块时,为什么程序中断没有减少?
我相信它不会减少,因为该内存已被赋予程序。
当我打电话时
free()
到底发生了什么?
那段内存被标记为可分配的,它以前的内容可以被覆盖。
考虑这个例子......
[allocatedStatus][sideOfAllocation][allocatedMemory]
^-- Returned pointer
考虑到这一点,free()
然后可以将 标记[allocatedStatus]
为 false,因此堆上的未来分配可以使用该内存。
它如何跟踪
free()
d 内存以便下次我声明malloc()
地址相同?
我认为不会。它只是扫描了一些空闲内存,发现前一个块被标记为空闲。
我相信,一旦您调用 free(),这完全取决于操作系统,它可能会选择立即回收该内存或不关心,并将该内存段标记为以后可能的获取(可能是同一件事)。据我所知,内存(如果重要)在 Windows 上的 free() 之后立即在任务管理器中显示为可用。
请记住,我们在这里谈论的内存是虚拟的。所以这意味着操作系统可以告诉你它想要的任何东西,并且可能不是机器物理状态的准确表示。
想想如果你正在编写一个操作系统,你将如何管理内存分配,你可能不想做任何可能会浪费资源的仓促操作。我们这里讨论的是 128 字节,你想浪费宝贵的处理时间单独处理它吗?这可能是这种行为的原因,也可能不是,至少是合理的。在一个循环中执行它,然后在另一个循环中执行 free() 或者只是分配大块内存,看看会发生什么,进行实验。
这是内存分配器如何工作的粗略想法:
你有一个分配器,它有一堆“bin”(“空闲列表”),它们只是空闲内存块的链接列表。每个 bin 都有与其相关联的不同块大小(即:您可以有一个包含 8 字节块、16 字节块、32 字节块等的列表……甚至是任意大小,如 7 或 10 字节块)。当您的程序请求内存(通常通过 malloc())时,分配器会转到适合您的数据的最小 bin 并检查其中是否有任何空闲内存块。如果不是,那么它将从操作系统请求一些内存(通常称为页面)并将它返回的块切割成一堆更小的块来填充 bin。然后它将这些空闲块之一返回给您的程序。
当您调用 free 时,分配器获取该内存地址并将其放回它来自的 bin(又名空闲列表)中,每个人都很高兴。:)
内存仍然可以使用,因此您不必保留分页内存,但就您的程序而言,它是免费的。