不释放内存是否可以接受

我正在研究一个应该从命令行使用的项目,其语法如下:

program-name input-file 

该程序应该处理输入,计算一些东西并在stdout上吐出结果。

我选择的语言是C ++,原因有几个我不想辩论。 计算阶段将是高度符号化的(思考编译器)并将使用非常复杂的动态分配数据结构。 特别是,它不适合RAII风格的编程。

我想知道忘记释放内存是否可以接受,因为我希望整个计算消耗的内存少于可用内存,并且程序完成后OS可以在一步中自由回收所有内存(假设程序终止)很快)。 你对此有何看法?

作为备份计划,如果我的项目需要以服务器或交互方式运行,我认为我总是可以将垃圾收集器重新编译为源代码。 有没有人有使用C ++垃圾收集器的经验? 他们运作良好吗?

在描述问题的具体情况下不应该引起任何问题。

但是,这并不完全正常。 静态分析工具会抱怨它。 最重要的是,它会制造坏习惯。

有时不释放内存是正确的做法。

我曾经写过编译器。 在构建解析树并遍历它以编写中间代码之后,我们只需退出。 解除分配树会有

  • 为编译器添加了一点慢,我们当然希望尽可能快。
  • 占用代码空间
  • 花时间编码和测试解除分配器
  • 违反了“没有代码执行比’没有代码’更好”的格言。

HTH! FWIW,这是“当天回归”,因为内存非虚拟且极小,盒子速度慢得多,前两个是非平凡的考虑因素。

我的感觉会像“WTF !!!”

这样看:

  • 您选择不包含垃圾收集器的编程语言,我们不允许问为什么。

  • 你基本上是说你懒得放心地释放记忆。

那么,WTF又来了。 懒惰不是任何事情的好理由,至少在没有释放它的情况下玩弄记忆。

只是释放内存,这是一个不好的做法,场景可能会改变,然后可能是一百万个原因,你可以需要释放内存,不做的唯一原因是懒惰,不养成坏习惯,习惯做事情是对的,那样你将来会倾向于做好!

不释放内存应该不是问题,但这是一个不好的做法。

Joel Coehoorn是对的:

它不应该导致任何问题。

但是,这并不完全正常。 静态分析工具会抱怨它。 最重要的是,它会制造坏习惯。

我还想补充一点,在编写代码时考虑重新分配可能比之后尝试改进代码要容易得多。 所以我可能会让它释放内存; 您不知道将来如何使用您的程序。

如果您想要一种非常简单的释放内存的方法,请查看Apache使用的“池”概念 。

好吧,我认为这是不可接受的。 你自己已经提到了潜在的未来问题。 不要认为他们一定很容易解决。

像“……因为我希望整个计算消耗更少……”这些都是着名的最后一句话。 同样,使用某些function重新编写代码是他们都在谈论和永远不会做的事情之一。

在短期内不释放内存可能听起来不错,但从长远来看可能会产生大量问题。 就个人而言,我认为这不值得。

有两种策略。 要么从一开始就构建GC设计。 这是更多的工作,但它会得到回报。 对于许多小对象,使用池分配器并跟踪内存池可能会付出代价。 这样,您可以跟踪内存消耗,并简单地避免许多类似代码但没有分配池的问题。

或者您从一开始就在整个程序中使用智能指针。 我实际上更喜欢这种方法,即使它使代码混乱。 一种解决方案是严重依赖模板,在引用类型时会占用大量冗余。

看一下像WebKit这样的项目。 他们的计算阶段类似于你的,因为他们为HTML构建解析树。 他们在整个程序中使用智能指针。

最后: “这是一个风格问题……邋work的工作往往会形成习惯。” – 大卫爱丁斯的巫术城堡中的丝绸。

将使用非常复杂的动态分配数据结构。 特别是,它不适合RAII风格的编程。

我几乎可以肯定这是懒惰编程的借口。 你为什么不能使用RAII? 是因为你不想跟踪你的分配,你没有指向它们的指针吗? 如果是这样,你如何期望使用分配的内存 – 总是有一个包含一些数据的指针。

是因为你不知道什么时候应该被释放? 将内存保留在RAII对象中,每个对象都被某些东西引用,当包含的对象被释放时,它们都会互相释放 – 如果你想将它作为一个服务器运行一天,每次迭代都是特别重要的。服务器有效运行一个“主”对象,其中包含所有其他对象,因此您可以删除它并且所有内存都会消失。 它还有助于防止您改装GC。

是因为你的所有内存都被分配并一直保持在使用中,并且只在最后被释放? 如果是这样,见上文。

如果你真的,真的想不出一个你不能泄漏内存的设计,至少要有使用私有堆的礼仪。 在您退出之前销毁该堆,如果有点’hacky’,您将拥有更好的设计。

有些情况下内存泄漏是可以的 – 静态变量,全局初始化数据,类似的东西。 但这些通常不大。

引用计数智能指针,如boost和TR1中的shared_ptr,也可以帮助您以简单的方式管理内存。

缺点是你必须包装使用这些对象的每个指针。

我以前做过这个,但发现很久以后,我需要程序能够在没有单独命令的情况下处理多个输入,或者程序的内容非常有用,需要将它们变成库程序可以从另一个预计不会终止的程序中多次调用。 后来重新设计并重新设计程序要比从一开始就使其无泄漏要困难得多。

因此,虽然您在描述要求时技术上是安全的,但我建议不要采用这种做法,因为您的要求可能有一天会发生变化。

如果你的程序的运行时间非常短,那应该不是问题。 然而,懒得释放你分配的内容并且忘记你分配的内容是两件完全不同的事情。 如果您只是丢失了轨道,那么该问自己是否真的知道您的代码对计算机做了什么。

如果你只是匆忙或懒惰,你的程序的生命相对于它实际分配的数量很小(例如,如果运行30秒,则每秒分配10 MB不小)..那么你应该没问题。

关于在程序退出时释放已分配的内存集的唯一“高尚”论点是否应该释放所有内容以防止valgrind抱怨泄漏,或者只是让操作系统执行此操作? 这完全取决于操作系统,如果您的代码可能成为库而不是短期运行的可执行文件。

运行期间的泄漏通常很糟糕,除非您知道您的程序将在很短的时间内运行,并且不会导致其他程序远比您的程序更重要,因为操作系统会打滑到脏分页。

你对此有何看法?

一些O / Ses可能无法回收内存,但我猜你不打算在那些O / Ses上运行。

作为备份计划,如果我的项目需要以服务器或交互方式运行,我认为我总是可以将垃圾收集器重新编译为源代码。

相反,我认为您可以生成一个子进程来执行脏工作,从子进程获取输出,让子进程在此之后尽快死掉,然后期望O / S进行垃圾回收。

我没有亲自使用过这个,但是既然你从头开始,你可能会考虑一下Boehm-Demers-Weiser保守的垃圾收集器

答案实际上取决于您的计划规模有多大以及需要展示的性能特征。 如果您从未释放内存,那么您的进程的内存占用量将远远大于其他情况。 取决于系统,这可能会导致大量分页并降低您或系统上其他应用程序的性能。

除此之外,上面所说的每个人都是正确的。 它可能不会在短期内造成伤害,但是你应该避免这种做法。 你将永远无法再使用该代码。 试图在之后改装GC将是一场噩梦。 只要考虑去每个地方你分配内存并试图改造它但不要破坏任何东西。

避免这样做的另一个原因是:声誉。 如果您未能解除分配,维护代码的每个人都会诅咒您的姓名,而您在公司的代表也会受到影响。 “你能相信他是多么愚蠢吗?看看这段代码。”

如果您确定在何处释放内存并非易事,我会担心数据结构操作的其他方面也可能无法完全理解。

除了操作系统(内核和/或C / C ++库)可以选择在执行结束时不释放内存这一事实外,您的应用程序应该始终提供适当的释放分配的内存作为一种良好的做法。 为什么? 假设您决定扩展该应用程序或重用代码; 如果您之前编写的代码在完成其工作后不必要地占用了内存,您很快就会遇到麻烦。 这是内存泄漏的一个秘诀。

总的来说,我同意这是一种不好的做法。

对于一次性程序,它可以没问题,但它有点像你不做你正在做的事情。

但是你的问题有一个解决方案 – 使用自定义分配器,它从malloc预分配更大的块,然后,在计算阶段之后,而不是释放自定义分配器中的所有小块,只需释放更大的预分配内存块。 然后,您无需跟踪解除分配所需的所有对象以及何时解除分配。 一个编写编译器的人也在很多年前向我解释了这种方法,所以如果它适用于他,它也可能适合你。

尝试在方法中使用自动变量,以便它们从堆栈中自动释放。

不释放堆内存的唯一有用的原因是节省了free()方法中使用的微量计算能力。 如果由于虚拟内存需求较大且物理内存资源较少而导致页面错误成为问题,您可能会失去任何优势。 需要考虑的一些因素是:

如果你要分配一些巨大的内存块或许多小块。

是否需要将内存锁定到物理内存中。

对于Win32系统,包括内存空洞和填充,你是否绝对肯定所需的代码和内存将适合2GB。

这通常是一个坏主意。 您可能会遇到某些情况,程序会尝试消耗比可用内存更多的内存。 此外,您可能无法启动该程序的多个副本。

如果您不关心上述问题,您仍然可以这样做。

退出程序时,分配的内存将automatically returned给系统。 因此,您可能无法释放已分配的内存。

但是,当您选择更大的程序(例如OSEmbedded systems ,解除分配是必要的,在这些程序中程序应该run forever ,因此小内存泄漏可能是恶意的。

因此,始终recommended to deallocate已分配的内存。