假设我有以下的C代码:
int main () {
int *p = malloc(10 * sizeof *p);
*p = 42;
return 0; //Exiting without freeing the allocated memory
}
当我编译并执行那个C程序时,也就是在分配一些内存空间之后,我退出应用程序并终止进程后,我所分配的内存是否仍然被保留(即基本上占据空间)?
假设我有以下的C代码:
int main () {
int *p = malloc(10 * sizeof *p);
*p = 42;
return 0; //Exiting without freeing the allocated memory
}
当我编译并执行那个C程序时,也就是在分配一些内存空间之后,我退出应用程序并终止进程后,我所分配的内存是否仍然被保留(即基本上占据空间)?
这取决于操作系统。大多数现代(以及所有主要的)操作系统将在程序终止时释放未被程序释放的内存。
依赖此情况是不好的实践,最好显式地释放它。问题不仅在于你的代码看起来不好。你可能决定将你的小程序集成到一个更大、长时间运行的程序中。然后过了一段时间,你需要花费几个小时去追踪内存泄漏。
依赖操作系统的特性也使得代码不太可移植。
一般来说,现代通用操作系统在终止进程后会进行清理。这是必要的,因为另一种情况是系统会随着时间流逝而失去资源,并需要重新启动,这是由于编写不良或仅偶尔发生泄漏资源的程序所致。
让程序显式释放其资源可以成为良好的实践,原因如下:
但是,以下是跳过释放内存的原因:高效的关闭。例如,假设您的应用程序包含一个大的内存缓存。如果在退出时它遍历整个缓存结构并逐个释放它,那么这没有任何用处并浪费资源。特别是要考虑到操作系统已经将包含缓存的内存页面交换到磁盘上的情况;通过遍历结构并释放它,您正在一次性地将所有这些页面带回内存,浪费了大量时间和能源而没有实际受益,并且可能会导致系统上的其他程序被交换出!
作为一个相关的例子,有一些高性能的服务器是通过为每个请求创建一个进程,并在完成后退出来工作的;这样它们甚至不需要跟踪内存分配,并且根本不进行释放或垃圾回收,因为所有东西都会在进程结束时消失到操作系统的自由内存中。(在进程内部可以使用自定义内存分配器来实现相同的功能,但需要非常谨慎的编程;基本上是在操作系统进程中创建自己的“轻量级进程”的概念。)很抱歉在上一篇帖子后这么晚才发帖。
另外,不是所有程序都能优雅地退出。 崩溃和ctrl-C等将导致程序以不受控制的方式退出。 如果您的操作系统没有释放堆、清理堆栈、删除静态变量等,您最终会因内存泄漏或更糟而使系统崩溃。
有趣的是,Ubuntu(我认为所有其他现代操作系统也是如此)中的崩溃/中断确实存在“处理过的”资源问题。 当程序结束/崩溃时,套接字、文件、设备等可能仍然“打开”。 在优雅退出之前关闭任何带有“句柄”或“描述符”的内容也是个好习惯。
我目前正在开发一个大量使用套接字的程序。 当我被挂住时,我必须ctrl-c退出,从而使我的套接字无法使用。 我添加了std :: vector以收集所有打开的套接字列表,以及一个sigaction处理程序来捕获sigint和sigterm。 处理程序遍历列表并关闭套接字。 我计划为在导致过早终止的throw之前使用的清理例程制作类似的清理程序。
有人愿意对这个设计发表评论吗?
是的。操作系统会清理资源。但是...旧版本的NetWare不会。
编辑:正如San Jacinto指出的那样,除了NetWare以外还有一些系统不会这样做。即使在一次性程序中,我也试图养成释放所有资源的习惯,只是为了保持这个好习惯。
malloc
只能承诺 C 语言对内存的处理方式;按设计,C 语言并未在 C 语言以外的行为方面做出任何保证。如果应用程序意外崩溃,运行时库作出的任何承诺都将无效,因为它已经无法实现这些承诺了。 - cHao这要看情况,操作系统通常会为您清理,但如果您正在开发嵌入式软件之类的项目,则可能不会被释放。
只需确保您释放它,这可以节省您后来在将其集成到大型项目中时的时间。
这实际上取决于操作系统,但对于您可能遇到的所有操作系统,内存分配将在进程退出时消失。
我认为直接释放内存是最好的。未定义的行为是最糟糕的事情,所以如果您在进程中仍然可以访问它时进行释放,那么请这样做,人们已经给出了很多充分的理由。
至于何时、或者是否要释放,我发现在W98中,真正的问题是“何时”(我没有看到强调这一点的帖子)。一个小型模板程序(用于MIDI SysEx输入,使用各种malloc'd空间)将在WndProc的WM_DESTROY位中释放内存,但当我将其移植到较大的程序中时,它在退出时崩溃了。我认为这意味着我试图释放操作系统已经在更大的清理过程中释放的内容。如果我在WM_CLOSE上执行它,然后调用DestroyWindow(),一切都很好,即时干净的退出。
虽然这与MIDI缓冲区不完全相同,但在保持进程完整、完全清理后再退出方面有相似之处。对于适度的内存块,这非常快速。我发现许多小缓冲区在操作和清理方面比较少的大缓冲区更快。
可能会存在例外情况,就像有人在避免从磁盘交换文件中拉出大内存块时所说的那样,但即使这样也可以通过保留更多、更小的分配空间来将其最小化。