退出C语言应用程序时,malloc分配的内存是否会自动释放?

114

假设我有以下的C代码:

int main () {
  int *p = malloc(10 * sizeof *p);
  *p = 42;
  return 0;  //Exiting without freeing the allocated memory
}

当我编译并执行那个C程序时,也就是在分配一些内存空间之后,我退出应用程序并终止进程后,我所分配的内存是否仍然被保留(即基本上占据空间)?


11
清理内存是“好的风格”,不是因为你可能在没有受保护内存的操作系统上运行(这是下面的主要建议),而是因为它增加了发现内存泄漏的可能性,使你的代码更加简洁和正确。 - Matt Joiner
我知道这只是一个例子,实际上并不重要,但如果你只存储一个整数,为什么要分配10个整数的内存? - flarn2006
这回答了你的问题吗? 当程序终止前不释放 malloc 时会发生什么? - bkausbk
9个回答

144

这取决于操作系统。大多数现代(以及所有主要的)操作系统将在程序终止时释放未被程序释放的内存。

依赖此情况是不好的实践,最好显式地释放它。问题不仅在于你的代码看起来不好。你可能决定将你的小程序集成到一个更大、长时间运行的程序中。然后过了一段时间,你需要花费几个小时去追踪内存泄漏。
依赖操作系统的特性也使得代码不太可移植。


21
我曾在一个嵌入式平台上遇到过Win98,基于那次经历,我可以说它在程序关闭时不会释放内存。 - San Jacinto
11
@Ken 这只是一个例子。另外,YAGNI和懒散的编码之间有一条界限。不释放资源会越过这条界限。YAGNI原则也应该应用于功能,而不是使程序正常工作的代码。(而不释放内存是一个bug)。 - Yacoby
5
最重要的考虑因素是,正如Yacoby所说的那样,内存管理是操作系统的一个特性。除非我错了,编程语言不定义程序执行前或执行后会发生什么。 - D.Shawley
10
手动释放内存需要更多的时间和代码,并且可能会引入错误(你确定你从未见过释放代码中的错误吗!)。有意忽略对于特定用例在所有方面都更劣并不是“懒惰”的行为。除非你要在一些无法在进程终止后自由页面的古老/微小系统上运行它,或将其集成到一个更大的程序中(YAGNI),否则这对我来说看起来是个净损失。我知道程序员认为自己不清理内存会伤害自己的自尊心,但实际上有什么实际好处呢? - Ken
6
任何在SO上提出内存泄漏的人都应该被剥夺所有声望和徽章。 - Ulterior
显示剩余14条评论

60

一般来说,现代通用操作系统在终止进程后会进行清理。这是必要的,因为另一种情况是系统会随着时间流逝而失去资源,并需要重新启动,这是由于编写不良或仅偶尔发生泄漏资源的程序所致。

让程序显式释放其资源可以成为良好的实践,原因如下:

  • 如果您有其他资源,在退出时操作系统不会对其进行清理,例如临时文件或任何外部资源状态的更改,那么您将需要编写代码以处理所有这些事情,并且这通常与释放内存优雅地结合起来。
  • 如果程序的寿命变长,则您不希望唯一的释放内存的方法是退出。例如,您可能希望将程序转换为保持运行并处理许多单元任务请求的服务器(守护程序),或者您的程序可能成为较大程序的一小部分。

但是,以下是跳过释放内存的原因:高效的关闭。例如,假设您的应用程序包含一个大的内存缓存。如果在退出时它遍历整个缓存结构并逐个释放它,那么这没有任何用处并浪费资源。特别是要考虑到操作系统已经将包含缓存的内存页面交换到磁盘上的情况;通过遍历结构并释放它,您正在一次性地将所有这些页面带回内存,浪费了大量时间和能源而没有实际受益,并且可能会导致系统上的其他程序被交换出!

作为一个相关的例子,有一些高性能的服务器是通过为每个请求创建一个进程,并在完成后退出来工作的;这样它们甚至不需要跟踪内存分配,并且根本不进行释放或垃圾回收,因为所有东西都会在进程结束时消失到操作系统的自由内存中。(在进程内部可以使用自定义内存分配器来实现相同的功能,但需要非常谨慎的编程;基本上是在操作系统进程中创建自己的“轻量级进程”的概念。)

15

很抱歉在上一篇帖子后这么晚才发帖。

另外,不是所有程序都能优雅地退出。 崩溃和ctrl-C等将导致程序以不受控制的方式退出。 如果您的操作系统没有释放堆、清理堆栈、删除静态变量等,您最终会因内存泄漏或更糟而使系统崩溃。

有趣的是,Ubuntu(我认为所有其他现代操作系统也是如此)中的崩溃/中断确实存在“处理过的”资源问题。 当程序结束/崩溃时,套接字、文件、设备等可能仍然“打开”。 在优雅退出之前关闭任何带有“句柄”或“描述符”的内容也是个好习惯。

我目前正在开发一个大量使用套接字的程序。 当我被挂住时,我必须ctrl-c退出,从而使我的套接字无法使用。 我添加了std :: vector以收集所有打开的套接字列表,以及一个sigaction处理程序来捕获sigint和sigterm。 处理程序遍历列表并关闭套接字。 我计划为在导致过早终止的throw之前使用的清理例程制作类似的清理程序。

有人愿意对这个设计发表评论吗?


2
我很高兴你说了这个,因为我有一个程序会留下套接字资源,我们的Ubuntu系统每两周需要重新启动或者内存开始耗尽,而且有足够的内存。如果忘记清理它们,我不确定系统资源是否被拆除。 - octopusgrabbus
12
Stackoverflow不是一个论坛;回答旧问题没有任何问题。http://meta.stackexchange.com/questions/20524/reviving-old-questions - mk12

9
在现代操作系统中,您的程序将运行在其自己的“进程”中。这是一个拥有自己地址空间、文件描述符等操作系统实体。您的malloc调用是从“堆”中分配内存,或者未分配的内存页面被分配给您的进程。
当您的程序结束时,所有分配给您的进程的资源都会被操作系统回收/销毁。在内存的情况下,分配给您的所有内存页面都被标记为“空闲”,并回收供其他进程使用。页面是比malloc处理更低级别的概念,因此,在整个清理过程中,malloc/free的具体细节都被简单地清除了。
这相当于,当您使用笔记本电脑并想要将其送给朋友时,您不需要逐个删除每个文件,而是格式化硬盘。
尽管如此,正如其他答案者所指出的那样,依赖这一点并不是好的做法:
1. 您应该始终编写代码以管理资源,在C中也包括内存。您可能最终会将您的代码嵌入到库中,或者它可能运行时间比您预期的长。
2. 一些操作系统(旧的和可能一些现代的嵌入式系统)可能不保持这样的严格进程边界,您的分配可能会影响其他进程的地址空间。

4

是的。操作系统会清理资源。但是...旧版本的NetWare不会。

编辑:正如San Jacinto指出的那样,除了NetWare以外还有一些系统不会这样做。即使在一次性程序中,我也试图养成释放所有资源的习惯,只是为了保持这个好习惯。


3
我没有给这篇文章点踩,但是对于后人来说,这篇文章相当危险。DOS仍然被用于许多嵌入式平台上,我非常怀疑它会为你做内存清理工作。这种一概而论的说法是错误的。 - San Jacinto
@San Jacinto:这是一个很好的观点。这就是为什么我提到了 NetWare,但可能需要澄清一下。我会稍微编辑一下。 - Mark Wilkins
4
DOS不是一个支持多任务的操作系统 - 当一个DOS程序(除了TSR)结束时,所有的内存都可以用于加载下一个程序。 - anon
@Neil 感谢你的提醒,但我指的是类似 TSR 的程序,它会在事件发生时启动,这是嵌入式系统的常见用途。无论如何,感谢您的专业知识和澄清,我做得不好 :) - San Jacinto

2
是的,当进程结束时,操作系统会释放所有内存。

我不明白为什么这个被踩了。malloc分配的内存会在进程结束时释放(维基百科对malloc的定义如此)。 - Arve
7
维基百科不是每个操作系统的手册。大多数 现代 操作系统会回收内存,但并非所有操作系统(尤其是旧的操作系统)都能够回收。此外,malloc 只能承诺 C 语言对内存的处理方式;按设计,C 语言并未在 C 语言以外的行为方面做出任何保证。如果应用程序意外崩溃,运行时库作出的任何承诺都将无效,因为它已经无法实现这些承诺了。 - cHao

2

这要看情况,操作系统通常会为您清理,但如果您正在开发嵌入式软件之类的项目,则可能不会被释放。

只需确保您释放它,这可以节省您后来在将其集成到大型项目中时的时间。


0

这实际上取决于操作系统,但对于您可能遇到的所有操作系统,内存分配将在进程退出时消失。


0

我认为直接释放内存是最好的。未定义的行为是最糟糕的事情,所以如果您在进程中仍然可以访问它时进行释放,那么请这样做,人们已经给出了很多充分的理由。

至于何时、或者是否要释放,我发现在W98中,真正的问题是“何时”(我没有看到强调这一点的帖子)。一个小型模板程序(用于MIDI SysEx输入,使用各种malloc'd空间)将在WndProc的WM_DESTROY位中释放内存,但当我将其移植到较大的程序中时,它在退出时崩溃了。我认为这意味着我试图释放操作系统已经在更大的清理过程中释放的内容。如果我在WM_CLOSE上执行它,然后调用DestroyWindow(),一切都很好,即时干净的退出。

虽然这与MIDI缓冲区不完全相同,但在保持进程完整、完全清理后再退出方面有相似之处。对于适度的内存块,这非常快速。我发现许多小缓冲区在操作和清理方面比较少的大缓冲区更快。

可能会存在例外情况,就像有人在避免从磁盘交换文件中拉出大内存块时所说的那样,但即使这样也可以通过保留更多、更小的分配空间来将其最小化。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接