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

问题描述 投票:5回答:4

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

我很困惑,为什么C中的free()方法会比在程序结束时回收堆的操作系统做任何不同的事情。

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

EG

这两个短节目会发生什么不同吗?

void main() {
    int* mem = malloc(1);
    return 0;
}
void main() {
    int* mem = malloc(1);
    free(mem);
    return 0;
}
c memory-management memory-leaks
4个回答
6
投票

不,像exitabort一样,终止程序不会像free那样回收记忆。当操作系统丢弃由freemalloc维护的数据时,使用free会导致一些最终无效的活动。

exit有一些复杂性,因为它不会立即终止该计划。现在,让我们考虑一下立即终止程序的效果,并考虑以后的并发症。

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

相反,当你调用free时,程序中的软件会运行,它必须查找你正在释放的内存大小,然后将有关该内存的信息插入到它正在维护的内存池中。可能有数千或数万(或更多)此类分配。一个释放其所有数据的程序可能必须执行数千次调用free。然而,最后,当程序退出时,free产生的所有更改都将消失,因为操作系统将丢弃有关该内存池的所有数据 - 所有数据都在内存页中,操作系统无法保留。

因此,在这方面,the answer you link to是正确的,称free是一种浪费。并且,正如它所指出的那样,必须遍历程序中的所有数据结构以获取它们中的指针,以便它们指向的内存可以被释放,从而导致所有这些数据结构在被换出时被读入内存到磁盘。对于大型程序,可能需要相当长的时间和其他资源。

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

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

回到exit,调用C exit例程并不会立即退出程序。调用退出处理程序(使用atexit注册),刷新流缓冲区,并关闭流。您调用的任何软件库可能已经设置了自己的退出处理程序,以便在程序退出时可以完成。所以,如果你想确保你的程序中使用的库在你结束程序时没有调用free,你必须调用abort,而不是exit。但通常优先的是优雅地结束程序,而不是通过中止。调用abort不会调用退出处理程序,刷新流,关闭流,或执行exit执行的其他减少代码 - 当程序调用abort时数据可能会丢失。

脚注

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

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

4
投票

这两个短节目会发生什么不同吗?

简单的答案是:它没有区别,在两种情况下都会将内存释放到系统中。调用free()并不是绝对必要的,并且会产生无限小的开销,但在尝试跟踪更复杂程序中的内存泄漏时可能会有用。

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

不完全是:

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

2
投票

free()是用户级内存管理功能,取决于您当前使用的malloc实现。用户级分配器可能维护内存块的链接列表,malloc / free将采用适当大小的块/将其放回。

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

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

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


2
投票

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

这两个短节目会发生什么不同吗?

我很困惑,为什么C中的free()方法会比在程序结束时回收堆的操作系统做任何不同的事情。

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

© www.soinside.com 2019 - 2024. All rights reserved.