退出前我应该释放内存吗?

43

如果我因为错误而退出程序,是否应该释放所有已分配的内存?

something = (char**) malloc (x * sizeof(char*));
for (i = 0; i < x; i++)
    something[i] = (char*) malloc (y + 1);

...

if (anything == NULL) {
   printf("Your input is wrong!");
   // should I free memory of every mallocated entity now?
   exit(1);
} 
else {
   // work with mallocated entities
   ...
   free(something); // it must be here
   system("pause);
}

3
我看不出有什么理由。操作系统会为您完成这项任务。 - Aleksandar Makragić
11
清理好自己的垃圾是一个好习惯。 - Eugene Sh.
4
虽然操作系统确实会为您完成这项工作,但当运算程序员添加新功能、再添加、再添加时会发生什么情况呢?或者更糟糕的是,当新的人员加入并开始修改代码时又该怎么办呢?我的建议是释放所有已分配的内存。哦,而且永远不要将您的分配结果强制转换。 - KevinDTimm
1
可能是程序退出时泄漏的内存是否被释放?的重复问题。 - Adrian McCarthy
1
不释放的一个优点是,如果你的程序有大量的分配,那么在退出时释放将会减慢应用程序的退出序列。 - M.M
显示剩余5条评论
5个回答

54
这实际上是一个非常难以回答的问题。
支持在退出前释放所有资源的优点:
  • 如果代码被重新排列,后续不会出现错误或内存泄漏
  • valgrind或内存泄漏检查器不会出现误报
  • 如果您在运行具有缺陷的操作系统或根本没有操作系统,则不会发生内存泄漏
反对在退出前释放所有资源的缺点:
  • 释放所有资源可能需要很多工作
  • 释放所有资源可能会引入错误和崩溃
  • 当您退出时,您的操作系统确实应该为您收回所有资源
还有一个观点(不确定是优点还是缺点):在大多数系统上,调用free并不会将内存返回给操作系统(只有退出才会这样做)。
最终,您必须决定哪些优缺点对您最重要。不同的程序员在不同的项目和不同的情况下会得出不同的结论;这里没有通用答案。

另请参阅此前的Stack Overflow问题。 另请参阅第7.24个问题C FAQ列表中。


8
说得好!重新表述我下面发表的评论:如果你是个新手,通常应该将“手动释放”作为习惯本能(更安全的习惯),而将“交给操作系统”作为一个有意识、知情且因情况而异的决策。这个决策过程会随着时间变成本能。否则,你会习惯性地忘记释放 malloc 分配的内存和清理悬空指针,结果在下次改变代码时,如果不释放内存就会产生意外行为。 - CodeMouse92
1
@JasonMc92,虽然听起来很不错,但这会给今天的代码增加额外的不可靠性。在应用程序终止时明确释放内存并不是免费的,也不安全,并且并非总是可能的。通常情况下,这是不必要的额外代码,需要在所有操作系统版本上进行测试和调试,反复测试和调试多次。这就像是一些单一的装饰语,比如“您必须在终止之前始终明确释放”,这导致我们使用的许多应用程序无法干净快速地关闭:( - Martin James
3
在另一个(更长的)评论链上我已经说过了:答案中的链接证实了我的观点。总体而言,手动释放(不仅在程序结束时)通常是稳定性问题(短期和长期),而不手动释放通常是性能问题,我们都知道那句格言:“过早优化是万恶之源。”(唐纳德·克努斯)。习惯性地释放内存,并决定在以后重构代码时不进行释放,这不是为了确保操作系统获取内存,而是为了防止错误发生。除此之外,我们存在一种“圣战”,最好同意不同意见。 - CodeMouse92
1
我发现在没有操作系统的情况下运行是最终没有内存泄漏的状态。当您的退出链加载下一个程序时,它会像您的程序一样启动,并将计算机中的所有内存视为随机初始化的空闲内存。 - Joshua
1
你可以认为,如果释放的工作量很大,那么这个程序设计得很差。 - klutt

15

在退出程序之前,您应该始终释放已分配的内存。正如其他答案中已经提到的那样,这将最小化静态或动态分析工具等产生的警告。

但是,您应该始终这样做的真正原因是因为释放通常会暴露应用程序中休眠的运行时错误。

如果您的某个地方存在导致内存破坏或更改指针地址的错误,则该错误可能会保持沉默和休眠状态。除非您完全更改了与错误不相关的东西,并因此重新排列内存布局。然后突然崩溃,而您甚至不知道为什么会崩溃,因为该错误甚至不在您刚添加的代码中。

通过释放内存,您可以引发这种错误的出现。因为如果堆或指向堆的指针有任何问题,您通常会在调用free()点处崩溃。这意味着您需要找到严重的错误,然后再发布程序。


2
这个答案很好,因为它超越了仅仅辩论这个主题中的“圣战”部分的想法。感谢@Lundin,提供了有价值的观点! - Per Lundberg

13

在程序结束之前,您无需释放内存。以任何方式终止程序都会自动释放所有内存。


7
在一个托管环境中,如果有适当的进程隔离,忽略共享内存等因素... - EOF
2
我想要给这个回答点个踩,因为这些东西最终会让我们后悔。虽然我没有点,但是我想点。 - KevinDTimm
3
@JasonMc92 我不同意。在错误退出时,正确清理所有内存通常是非常困难或不可能的,因为数据结构可能已经损坏。试图清理事情只是徒劳无功。 - fuz
3
我认为我的意思是:我们需要默认手动清理,并在适当时候做出有意识的决定将其留给操作系统来处理。如果我们默认选择“让操作系统处理”,并且必须自觉决定自己进行清理,那么我们更容易遗漏一些导致悬空指针等问题(特别是在代码重写过程中),从而产生UB(未定义行为)。不手动清理的决定需要是一种教育、有意识的选择,我相信对于你们而言这已经成为惯例了。不太有经验的编码人员需要养成这个习惯。 - CodeMouse92
3
养成这种习惯的方法,当然是在可能的情况下习惯性地计划手动清理,并且当他们意识到这将是困难/不切实际/危险时,做出“我最好让操作系统来清理”的有意识决定。相反的习惯是,许多新手编码人员会决定“我不需要释放我的malloc。操作系统会做到!”并没有注意到他们五行后要求一群鼻子恶魔的问题。 - CodeMouse92
显示剩余12条评论

12

这取决于操作系统。最佳实践是明确地释放它。如果你的内存到处都没有被释放,那么使用像valgrind这样的工具就会非常麻烦,我无法分辨什么是好的,什么是坏的等等。

即使在一个显式释放内存的操作系统上,你仍然会遇到其他资源的问题。随着你的应用程序不断增长并引入第三方库,你可能会出现资源泄漏的情况。比如,我编写了一个需要调用关闭处理程序的库,而这个处理程序支持暂时文件,除非你调用关闭,否则它将不会被删除。或者我已经分离了一些在后台运行的进程,并且使用信号或某些其他资源来管理它们,而你并不知道。


5
哪些系统无法在程序终止后释放内存? - fuz
1
请查看这里的评论... https://dev59.com/FXA85IYBdhLWcg3wBO7h 和这里。https://dev59.com/K2w15IYBdhLWcg3wQ5hi - Harry
5
处理临时文件的方法是在运行时删除它们,而不是在关机时删除。电源随时可能中断,依赖于关机程序的应用程序将会遇到麻烦。 - Martin James
1
@Harry 如果你正在为这样的系统编程,那么你应该知道这一点,并不会在Stack Overflow上问这种问题。 - fuz
2
@MartinJames 两个都做?不留下任何混乱,抵制原则和一切。 - Sled

0
我遇到了完全相反的情况:来自第三方库的静态对象析构函数导致段错误。这是因为在退出之前显式释放了内存池。 我认为,更好的做法是合理地关注程序结构。

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