终止程序是否以与free()相同的方式回收内存?

时间:2019-04-21 01:54:36

标签: c memory-management memory-leaks

我看到this answer是一个栈溢出问题,它说在c程序的最后释放内存实际上是有害的,因为它将无法使用的变量移到系统内存中。

我很困惑,为什么C中的free()方法会执行与操作系统在程序结尾处回收堆不同的任何事情。

有人知道在内存管理方面free()和终止之间是否存在真正的区别,如果有,操作系统如何将这两个区别对待?

例如

这两个简短程序之间会发生什么不同吗?

void main() {
    int* mem = malloc(1);
    return 0;
}
void main() {
    int* mem = malloc(1);
    free(mem);
    return 0;
}

4 个答案:

答案 0 :(得分:6)

否,与exitabort一样终止程序不会以与free相同的方式回收内存。使用free会导致某些活动,当操作系统丢弃mallocfree维护的数据时,这些活动最终将无效。

exit有一些复杂之处,因为它不会立即终止程序。现在,让我们考虑立即终止程序的效果,然后再考虑以后的复杂性。

在通用多用户操作系统中,当进程终止时,操作系统会释放其用于其他目的的内存。 1 在很大程度上,这仅意味着操作系统系统执行一些会计操作。

相反,当您调用free时,程序中的软件将运行,并且它必须查找要释放的内存的大小,然后将有关该内存的信息插入其正在维护的内存池中。这样的分配可能有成千上万。释放所有数据的程序可能必须执行对free的数千次调用。但是,最后,当程序退出时,free产生的所有更改都将消失,因为操作系统将丢弃有关该内存池的所有数据-所有数据都位于该操作的内存页中系统不保留。

因此,就此而言,the answer you link to是正确的,调用free是浪费。而且,正如所指出的那样,必须遍历程序中的所有数据结构以获取它们中的指针,以便它们所指向的内存可以被释放,如果这些数据结构已被换出,则它们将被读入内存中。到磁盘。对于大型程序,可能需要花费大量时间和其他资源。

另一方面,不清楚是否很容易避免多次调用free。这是因为释放内存并不是终止程序唯一需要清除的内容。程序可能希望将最终数据写入文件或将最终消息发送到网络连接。此外,程序可能没有直接建立所有这些上下文。大多数大型程序都依赖于软件层,并且每个软件包可能都设置了自己的上下文,并且通常没有提供任何方法告诉其他软件“我现在要退出。完成有价值的上下文,但是跳过所有的内存释放。”因此,所有需要的清理任务可能会与自由内存任务交织在一起,并且可能没有解开它们的好方法。

通常应编写软件,以便在程序突然中止时不会有任何可怕的事情发生(因为这可能是由于断电而引起的,而不仅仅是故意的用户操作)。但是,即使程序可以忍受中止,但正常退出仍然会有价值。

回到exit,调用C exit例程不会立即退出程序。调用退出处理程序(在atexit中注册),刷新流缓冲区,并关闭流。您调用的所有软件库都可能设置了自己的退出处理程序,以便它们可以在程序退出时完成。因此,如果您要确保程序中使用的库在结束程序时没有调用free,则必须调用abort,而不是exit。但是通常最好以优雅的方式结束程序,而不是中止程序。调用abort不会调用出口处理程序,刷新流,关闭流或执行exit所做的其他结束代码-程序调用{​​{1}}时数据可能会丢失。

脚注

1 释放内存并不意味着它立即可用于其他目的。具体结果取决于内存的每一页。例如:

  • 如果与其他进程共享内存,则它们仍然需要该内存,因此,释放该进程使用该内存只会减少使用该内存的进程数。它不能立即用于任何其他用途。
  • 如果该内存未由任何其他进程使用,但包含从磁盘上的文件映射的数据,则操作系统可能会在需要时将其标记为可用,但暂时不予处理。这是因为您可能会再次运行同一程序,并且如果数据仍在内存中会很好,那么为什么不把它留在原地以防万一呢?该数据甚至可能由使用相同文件的其他程序使用。 (例如,许多程序可能使用相同的共享库。)
  • 如果该内存未由任何其他进程使用,而只是被程序用作工作区,而不是从文件映射而来,则系统可能会将其标记为立即可用并且不包含任何有用的东西。

答案 1 :(得分:4)

  

这两个简短程序之间会发生什么不同吗?

简单的答案是:这没有什么区别,两种情况下内存都释放给系统。严格来说,调用border-radius并不是必须的,并且确实会带来无穷的开销,但是在尝试跟踪更复杂的程序中的内存泄漏时可能会很有用。

  

终止程序是否以与free()相同的方式回收内存?

不完全是:

  • 终止程序会释放该程序使用的内存,无论是用于程序代码,数据,堆栈还是堆。它还释放了一些其他资源,例如文件句柄,设备句柄,网络套接字...,无论free分配了多少内存块,这一切都可以高效完成。
  • 相反,malloc()使程序可以进一步使用内存块供以后对free()malloc()的调用。根据其大小和堆的实现,此释放的块可能会或可能不会返回给操作系统供其他程序使用。同样值得一提的是碎片问题,在该问题中,释放的内存小块可能无法用于较大的分配,因为它们被分配的块包围。 C堆不执行打包或碎片整理,它只是合并相邻的空闲块。在离开程序之前释放所有分配的块可能对调试有用,但可能很复杂且很耗时,而对于程序终止后由系统重用内存不是必需的。

答案 2 :(得分:2)

free()是用户级别的内存管理功能,取决于您当前正在使用的malloc实现。用户级分配器可能会维护内存块的链表,而malloc / free将获取适当大小的块/将其放回原处。

exit()销毁地址空间和所有区域。 这与malloc堆以及用于管理进程地址空间的其他一些区域和内核数据结构有关:

  

每个地址空间由许多页面对齐的区域组成   正在使用的内存数量。它们从不重叠并代表一组   包含彼此相关的页面的地址   保护条款和目的。这些区域由   struct vm_area_struct,大致类似于   BSD中的vm_map_entry结构。为了清楚起见,区域可以代表   与malloc()一起使用的进程堆,malloc()是内存映射文件,例如   共享库或分配给它的匿名内存块   mmap()。该区域的页面可能仍然需要分配,例如   活动和居民或已被调出页面

参考:https://www.kernel.org/doc/gorman/html/understand/understand007.html

答案 3 :(得分:2)

精心设计的程序在退出时释放内存的原因是为了检查内存泄漏。如果您的应用程序级内存分配在上次取消分配之后没有变为零,则说明您的内存存在未被正确管理的内存,并且代码中可能存在内存泄漏。

  

这两个简短程序之间会发生什么不同吗?

  

我很困惑,为什么C中的free()方法会执行与操作系统在程序结尾处回收堆不同的任何事情。

操作系统在页面中分配内存。堆管理器(例如malloc / free实现)从操作系统分配页面,并将页面细分为较小的分配。调用free()通常会将内存返回到堆中。他们不会将页面返回到操作系统。