退出C应用程序时,是否自动释放了malloc-ed内存?

And*_*ech 85 c memory-management

假设我有以下C代码:

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

当我编译并执行该C程序时,即在内存中分配一些空间之后,在我退出应用程序并且进程终止后,我分配的内存是否仍会被分配(即基本上占用空间)?

Yac*_*oby 104

这取决于操作系统.大多数现代(和所有主要)操作系统将释放程序结束时未被程序释放的内存.

依赖于此是不好的做法,最好明确地释放它.问题不仅在于您的代码看起来很糟糕.您可能决定将小程序集成到一个较大的,长期运行的程序中.然后一段时间后,你必须花费数小时追踪内存泄漏.
依赖于操作系统的功能也使代码不那么便携.

  • 我曾经在嵌入式平台上遇到win98,基于这种体验,我可以说当程序关闭时它不会释放内存. (13认同)
  • @Ken这是一个例子.此外,YAGNI与草率编码之间存在界限.没有释放资源穿过它.YAGNI原则也适用于功能,而不是使程序正常工作的代码.(而不是释放内存是一个错误). (8认同)
  • 手动释放内存需要更多时间,需要更多代码,并引入了错误的可能性(告诉我你从未见过解除分配代码中的错误!).故意忽略某些对你的特定用例来说更糟糕的东西并不"草率".除非或者直到你的意思是在一个古老/微小的系统上运行它,它不能在进程终止后释放页面,或者将它集成到一个更大的程序(YAGNI)中,它对我来说看起来像是一个净损失.我知道让一个程序员的自我感到痛苦,想到不要自己清理它,但实际上它实际上更好吗? (8认同)
  • 任何提出内存泄漏的人都应该剥夺所有声誉和徽章 (6认同)
  • +1:最重要的考虑因素是内存管理正如Yacoby所说:_"操作系统的一个特性"_.除非我弄错了,否则编程语言不会定义程序执行之前或之后发生的事情. (5认同)
  • @Ken:我的很多经验都是在_unhosted_环境中,因此通常意义上没有操作系统 - 有一个小内核可以提供一些消息传递和任务控制,但在服务方面却更多.如果您不释放资源,它们将永远不会被释放.我认为不释放你所获得的资源是傲慢的.假设_其他人会为你做这件事_并不是我容易支持的习惯. (5认同)
  • 支持虚拟内存的操作系统不会遇到显式释放内存的麻烦.它是虚拟的,它只是不再存在. (3认同)
  • D.Shawley:编程语言也没有定义系统调用或文件系统.仅使用编程语言规范所定义的程序是完全可移植的,并且实际上是无用的. (3认同)
  • 轶事证据为什么显式释放可能是坏的:我写了一个命令行实用程序,它构建了一个巨大的链表; 释放节点时退出延迟程序终止几秒钟(!),而不这样做允许操作系统批量回收内存... (2认同)
  • @Ken:没有更好的方法可以通过释放内存来找出代码破坏堆的方法.在堆损坏上翻转忽略位是不明智的.我认为没有在Release版本中清理是可以的. (2认同)
  • D.Shawley:没有操作系统,你必须手工做很多事情.然而,听起来这个问题的主人并不是这样. (2认同)
  • 我和肯在这一个.在最坏的情况下,解除分配可能是涉及线程之间同步的极其困难的问题,并且如果除了程序退出之外不会发生解除分配的情况,则不值得编写/维护它的成本以及错误的风险让一些理论家高兴... (2认同)

Kev*_*eid 43

通常,现代通用操作系统在终止进程后进行清理.这是必要的,因为选择是让系统随着时间的推移失去资源和需要重新启动由于这是写得不好或根本就很少发生的那泄漏资源错误的程序.

无论如何,让您的程序明确地释放其资源可能是出于各种原因的良好实践,例如:

  • 如果您有其他资源在退出时未被操作系统清除,例如临时文件或对外部资源状态的任何类型更改,那么您将需要在退出时处理所有这些内容的代码,通常优雅地结合释放记忆.
  • 如果您的程序开始具有更长的生命周期,那么您将不希望释放内存的唯一方法是退出.例如,您可能希望将程序转换为服务器(守护程序),该服务器在处理对单个工作单元的许多请求时保持运行,或者您的程序可能成为较大程序的一小部分.

但是,这是跳过释放内存的原因:高效关机.例如,假设您的应用程序在内存中包含一个大缓存.如果它退出时它会通过整个缓存结构并一次释放一个,这没有任何用处,浪费资源.特别是,考虑操作系统将包含缓存的内存页面交换到磁盘的情况; 通过走结构并释放它,你将所有这些页面一次性地带回到内存中,浪费了大量的时间和精力,没有实际的好处,甚至可能导致系统上的其他程序被换掉!

作为一个相关的例子,有一些高性能服务器通过为每个请求创建一个进程来工作,然后在完成时退出它; 通过这种方式,他们甚至不必跟踪内存分配,并且根本不进行任何释放或垃圾收集,因为在进程结束时所有内容都会消失回操作系统的可用内存.(在使用自定义内存分配器的进程中可以完成同样的事情,但需要非常仔细的编程;基本上在OS进程中制定自己的"轻量级进程"概念.)


Wes*_*ler 7

我很抱歉在最后一篇帖子发布到这个帖子后这么久了.

还有一点.并非所有程序都能让它出色地退出.崩溃和ctrl-C等将导致程序以不受控制的方式退出.如果您的操作系统没有释放堆,清理堆栈,删除静态变量等,最终会导致系统崩溃或内存泄漏.

有趣的是,Ubuntu中的崩溃/中断,我怀疑所有其他现代操作系统,确实存在"处理"资源的问题.当程序结束/崩溃时,套接字,文件,设备等可以保持"打开".在优雅退出之前,作为清理工作的一部分,使用"手柄"或"描述符"关闭任何东西也是一种很好的做法.

我目前正在开发一个大量使用套接字的程序.当我陷入困境时,我必须从中调出ctrl-c,因此,搁置我的插座.我添加了一个std :: vector来收集所有打开的套接字列表和一个捕获sigint和sigterm的sigaction处理程序.处理程序遍历列表并关闭套接字.我计划在抛出前使用类似的清理程序,这将导致提前终止.

有人关心这个设计吗?

  • Stackoverflow不是一个论坛; 回答旧问题没有*错误.http://meta.stackexchange.com/questions/20524/reviving-old-questions (7认同)
  • 我很高兴你这么说,因为我有一个程序会留下套接字资源,而我们的 Ubuntu 系统需要每两周重新启动一次,否则内存开始耗尽,并且有足够的内存。如果您忘记清理它们,我不确定系统资源是否会被拆除。 (2认同)

Ben*_*tto 6

这里发生的事情(在现代操作系统中)是你的程序在自己的"进程"中运行.这是一个操作系统实体,具有自己的地址空间,文件描述符等.您的malloc调用是从"堆"分配内存,或分配给您的进程的未分配内存页.

当您的程序结束时,如本示例所示,分配给您的流程的所有资源都只是由操作系统回收/拆除.在内存的情况下,分配给您的所有内存页面都只是标记为"空闲"并循环使用其他进程.页面是一个比malloc处理的更低层次的概念 - 因此,当整个事物被清理时,malloc/free的细节都被简单地冲走了.

这是道德等同的,当你完成使用你的笔记本电脑并想把它交给朋友时,你不必费心去个别删除每个文件.你只需格式化硬盘.

所有这些都说,正如所有其他回答者所指出的那样,依靠这一点并不是一种好的做法:

  1. 你应该总是编程来处理资源,在C中也意味着内存.您可能最终将代码嵌入到库中,或者最终运行时间可能比预期的要长.
  2. 某些操作系统(较旧的操作系统,可能是一些现代嵌入式系统操作系统)可能无法维持此类硬处理边界,并且您的分配可能会影响其他人的地址空