238

在您的 C 或 C++ 应用程序中存在内存泄漏是否可以接受?

如果您分配一些内存并使用它直到应用程序中的最后一行代码(例如,全局对象的析构函数)怎么办?只要内存消耗不随时间增长,是否可以相信操作系统在应用程序终止时(在 Windows、Mac 和 Linux 上)为您释放内存?如果内存被连续使用直到被操作系统释放,你甚至会认为这是一个真正的内存泄漏。

如果第三方库将这种情况强加给您怎么办?是否会拒绝使用该第三方库,无论它有多么出色?

我只看到一个实际的缺点,那就是这些良性泄漏会在内存泄漏检测工具中显示为误报。

4

50 回答 50

337

不。

作为专业人士,我们不应该问自己的问题是:“这样做可以吗?” 而是“是否有充分的理由这样做?” 并且“寻找内存泄漏是一种痛苦”并不是一个好的理由。

我喜欢让事情变得简单。简单的规则是我的程序不应该有内存泄漏。

这也让我的生活变得简单。如果我检测到内存泄漏,我会消除它,而不是通过一些复杂的决策树结构来确定它是否是“可接受的”内存泄漏。

它类似于编译器警告——警告对我的特定应用程序是否致命?也许不吧。

但这最终是一个专业纪律的问题。容忍编译器警告和容忍内存泄漏是一个坏习惯,最终会在后面咬我。

把事情推到一个极端,外科医生将一些手术设备留在病人体内是可以接受的吗?

尽管可能会出现移除该设备的成本/风险超过将其留在其中的成本/风险的情况,并且可能存在无害的情况,如果我在 SurgeonOverflow.com 上看到此问题并看到除“不”之外的任何答案,这将严重破坏我对医学界的信心。

——</p>

如果第三方图书馆将这种情况强加于我,我会严重怀疑该图书馆的整体质量。就好像我试驾了一辆车,在一个杯架上发现了几个松动的垫圈和螺母——这本身可能没什么大不了的,但它表现出对质量的承诺不足,所以我会考虑替代品。

于 2008-11-07T19:20:25.530 回答
82

我不认为这是内存泄漏,除非“使用”的内存量不断增长。拥有一些未释放的内存虽然并不理想,但也不是什么大问题,除非所需的内存量不断增长。

于 2008-11-07T19:06:51.423 回答
79

首先让我们的定义正确。内存泄漏是当内存被动态分配时,例如使用malloc(),并且所有对内存的引用都会丢失而没有相应的空闲。一个简单的方法是这样的:

#define BLK ((size_t)1024)
while(1){
    void * vp = malloc(BLK);
}

请注意,每次 while(1) 循环时,都会分配 1024 (+overhead) 字节,并将新地址分配给 vp; 没有剩余的指向前一个 malloc 块的指针。这个程序保证在堆用完之前一直运行,并且没有办法恢复任何 malloc 的内存。内存从堆中“泄漏”出来,再也见不到了。

不过,你所描述的听起来像

int main(){
    void * vp = malloc(LOTS);
    // Go do something useful
    return 0;
}

您分配内存,使用它直到程序终止。这不是内存泄漏;它不会损害程序,并且在程序终止时会自动清除所有内存。

通常,您应该避免内存泄漏。首先,因为就像你的高度和机库的燃料一样,已经泄漏且无法恢复的内存是无用的;其次,在开始时正确编码而不是泄漏内存比后来发现内存泄漏要容易得多。

于 2008-11-08T06:02:47.120 回答
41

理论上没有,实际上取决于.

这实际上取决于程序正在处理多少数据,程序运行的频率以及它是否持续运行。

如果我有一个读取少量数据的快速程序进行计算并退出,则永远不会注意到小的内存泄漏。因为程序运行时间不长,只使用少量内存,所以泄漏会很小,当程序存在时会被释放。

另一方面,如果我有一个程序可以处理数百万条记录并运行很长时间,那么如果有足够的时间,一个小的内存泄漏可能会导致机器停机。

至于有泄漏的第三方库,如果它们引起问题,要么修复库,要么找到更好的替代方案。如果它不会引起问题,那真的很重要吗?

于 2008-11-07T19:05:12.267 回答
37

很多人的印象似乎是,一旦释放内存,它就会立即返回操作系统并可供其他程序使用。

这不是真的。操作系统通常以 4KiB 页面管理内存。malloc和其他类型的内存管理从操作系统获取页面并在他们认为合适的时候对它们进行子管理。假设您的程序稍后会分配更多内存,则很可能free()不会将页面返回给操作系统。

我并不是说free()永远不会将内存返回给操作系统。它可能会发生,尤其是当您释放大量内存时。但不能保证。

重要的事实:如果您不释放不再需要的内存,则可以保证进一步的 malloc 会消耗更多的内存。但是如果你先释放,malloc 可能会重新使用释放的内存。

这在实践中意味着什么?这意味着如果您知道您的程序从现在开始不再需要任何内存(例如,它处于清理阶段),那么释放内存就不是那么重要了。但是,如果程序稍后可能会分配更多内存,则应避免内存泄漏——尤其是可能重复发生的内存泄漏。

另请参阅此评论以获取有关为什么在终止前释放内存不好的更多详细信息。

评论者似乎不明白调用free()不会自动允许其他程序使用释放的内存。但这就是这个答案的全部意义!

所以,为了说服人们,我将展示一个 free() 没有什么好处的例子。为了使数学易于理解,我将假设操作系统管理 4000 字节页面中的内存。

假设您分配了一万个 100 字节的块(为简单起见,我将忽略管理这些分配所需的额外内存)。这会消耗 1MB 或 250 页。如果您随后随机释放 9000 个这些块,那么您只剩下 1000 个块 - 但它们分散在各处。据统计,大约有 5 个页面是空的。其他 245 个将每个都至少有一个分配的块。这相当于 980KB 的内存,操作系统不可能回收 - 即使您现在只分配了 100KB!

另一方面,您现在可以 malloc() 再增加 9000 个块,而不会增加程序占用的内存量。

即使在技术上free()可以将内存返回给操作系统,它也可能不会这样做。需要在快速运行和节省内存之间取得平衡。此外,一个已经分配了大量内存然后释放它的程序很可能会再次这样做。Web 服务器需要在一个又一个请求后处理请求 - 保持一些“松弛”内存可用是有意义的,这样您就不需要一直向操作系统询问内存。free()

于 2008-11-10T01:05:26.207 回答
27

在应用程序运行后清理操作系统在概念上没有任何问题。

这实际上取决于应用程序及其运行方式。必须注意需要运行数周的应用程序中持续发生的泄漏,但是一个无需太高内存需求即可计算结果的小工具应该不是问题。

许多脚本语言不垃圾收集循环引用是有原因的……对于它们的使用模式,这不是一个实际的问题,因此与浪费的内存一样浪费资源。

于 2008-11-07T19:08:27.877 回答
19

我相信答案是否定的,绝不允许内存泄漏,而且我有一些我没有看到明确说明的原因。这里有很好的技术答案,但我认为真正的答案取决于更多的社会/人类原因。

(首先,请注意,正如其他人所提到的,真正的泄漏是指您的程序在任何时候都丢失了它已分配的内存资源的跟踪。在 C 中,当您malloc()指向一个指针并让该指针离开范围而没有执行free()第一的。)

您在这里决定的重要症结是习惯。当您使用使用指针的语言进行编码时,您将大量使用指针。指针很危险;它们是将各种严重问题添加到代码中的最简单方法。

当你编写代码时,有时你会忙得不可开交,有时你会感到疲倦、生气或担心。在那些有点分心的时候,你在自动驾驶仪上编码更多。自动驾驶效果不会区分一次性代码和大型项目中的模块。在那些时候,你建立的习惯最终会出现在你的代码库中。

所以不,永远不要允许内存泄漏,因为即使你是路上唯一的汽车,你仍然应该在改变车道时检查你的盲点。在你活跃的大脑分心的时候,良好的习惯可以让你免于灾难性的失误。

除了“习惯”问题之外,指针是复杂的,并且通常需要大量的脑力来进行心理跟踪。在使用指针时,最好不要“搅浑水”,尤其是当您不熟悉编程时。

还有更多的社交方面。通过正确使用malloc()and free(),任何查看您的代码的人都会感到放心;你正在管理你的资源。但是,如果您不这样做,他们会立即怀疑存在问题。

也许你已经知道在这种情况下内存泄漏不会伤害任何东西,但是当你的代码的每个维护者阅读那段代码时,他也必须在脑海中解决这个问题。通过使用free(),您甚至无需考虑问题。

最后,编程是用一种明确的语言编写一个过程的心智模型,以便人和计算机可以完美地理解所述过程。良好编程实践的一个重要部分是永远不要引入不必要的歧义。

智能编程既灵活又通用。糟糕的编程是模棱两可的。

于 2008-11-08T18:55:16.733 回答
18

我将给出一个不受欢迎但实用的答案,即释放内存总是错误的,除非这样做会减少程序的内存使用量。例如,一个程序进行单个分配或一系列分配来加载它将在其整个生命周期中使用的数据集,不需要释放任何东西。在具有非常动态内存需求的大型程序的更常见情况下(想想网络浏览器),您显然应该尽快释放不再使用的内存(例如关闭选项卡/文档/等) ,但是当用户选择点击“退出”时,没有理由释放任何东西,这样做实际上是对用户体验有害的。

为什么?释放内存需要触摸内存。即使您的系统的 malloc 实现碰巧没有在分配的内存块附近存储元数据,您也可能会遍历递归结构以找到您需要释放的所有指针。

现在,假设您的程序处理了大量数据,但有一段时间没有触及其中的大部分数据(同样,Web 浏览器就是一个很好的例子)。如果用户正在运行大量应用程序,则该数据的很大一部分可能已交换到磁盘。如果你只是 exit(0) 或从 main 返回,它会立即退出。很棒的用户体验。如果您不小心尝试释放所有内容,您可能会花费 5 秒或更长时间将所有数据交换回,然后立即将其丢弃。浪费用户的时间。浪费笔记本电脑的电池寿命。浪费硬盘的磨损。

这不仅仅是理论上的。每当我发现自己加载了太多应用程序并且磁盘开始抖动时,我什至不会考虑单击“退出”。我尽可能快地到达终端并输入 killall -9 ...因为我知道“退出”只会让情况变得更糟。

于 2010-06-28T06:11:39.970 回答
15

我认为在你的情况下,答案可能是没关系。但是您绝对需要证明内存泄漏是一个有意识的决定。您不希望维护程序员出现,将您的代码放入函数中,然后调用它一百万次。因此,如果您决定泄漏是可以的,您需要将其记录下来(用大写字母),以供将来可能需要处理该程序的任何人使用。

如果这是第三方库,您可能会被困。但绝对记录发生这种泄漏。

但基本上,如果内存泄漏是一个已知数量,如 512 KB 缓冲区或其他东西,那么这不是问题。如果内存泄漏一直在增长,就像每次调用库时一样,你的内存增加了 512KB 并且没有被释放,那么你可能会遇到问题。如果您记录它并控制调用执行的次数,它可能是易于管理的。但是,您确实需要文档,因为虽然 512 并不多,但超过一百万次调用的 512 却很多。

您还需要检查您的操作系统文档。如果这是嵌入式设备,则可能存在操作系统不会从退出的程序中释放所有内存。我不确定,也许这不是真的。但值得研究。

于 2008-11-07T19:15:27.740 回答
11

我敢肯定,有人会想出一个理由说“是”,但不会是我。我不会说不,而是说这不应该是一个是/否的问题。有一些方法可以管理或控制内存泄漏,许多系统都有这些方法。

离开地球的设备上有 NASA 系统为此计划。系统会每隔一段时间自动重启一次,这样内存泄漏就不会对整个操作造成致命的影响。只是一个遏制的例子。

于 2008-11-07T19:07:13.823 回答
8

如果您分配内存并使用它直到程序的最后一行,那不是泄漏。如果您分配内存并忘记它,即使内存量没有增长,这也是一个问题。已分配但未使用的内存可能会导致其他程序运行速度变慢或根本不运行。

于 2008-11-07T19:13:32.937 回答
8

一方面,我可以数出我在一段时间内看到的“良性”泄漏的数量。

所以答案是非常合格的yes。

一个例子。如果您有一个需要缓冲区来存储循环队列或双端队列的单例资源,但不知道缓冲区需要多大,并且无法承担锁定或每个读取器的开销,那么分配一个指数倍增的缓冲区但是不释放旧的将泄漏每个队列/双端队列的有限内存量。这样做的好处是它们极大地加速了每次访问,并且可以通过永远不会冒着争用锁的风险来改变多处理器解决方案的渐近性。

我已经看到这种方法对于具有非常明确固定计数的事物(例如每 CPU 工作窃取双端队列)非常有用,并且在用于保存/proc/self/mapsHans Boehm 的 C 的保守垃圾收集器中的单例状态的缓冲区中的程度要小得多/C++,用于检测根集等。

虽然从技术上讲是泄漏,但这两种情况的大小都是有限的,并且在可增长的循环工作窃取双端队列的情况下,有巨大的性能提升,以换取队列的内存使用量增加 2 倍。

于 2008-11-07T20:41:16.457 回答
8

如果您在程序开始时分配了一堆堆,并且在退出时没有释放它,那么这本身就不是内存泄漏。内存泄漏是指您的程序在一段代码上循环,并且该代码分配了堆,然后“丢失轨道”而不释放它。

事实上,没有必要在退出之前调用 free() 或 delete。当进程退出时,它的所有内存都被操作系统回收(POSIX 肯定是这种情况。在其他操作系统上——尤其是嵌入式操作系统——YMMV)。

我在退出时不释放内存的唯一警告是,如果你重构你的程序,例如,它变成一个等待输入的服务,不管你的程序做什么,然后循环等待另一个服务调用,那么您编码的内容可能会变成内存泄漏。

于 2008-11-07T20:44:41.350 回答
6

在这种问题中,上下文就是一切。就我个人而言,我无法忍受泄漏,并且在我的代码中,如果它们突然出现,我会竭尽全力修复它们,但修复泄漏并不总是值得的,而且当人们有时按小时付费时告诉他们修复他们代码中的漏洞不值得我花钱。让我给你举个例子:

我正在对一个项目进行分类,做一些性能工作并修复了很多错误。在我追踪并完全理解的应用程序初始化期间存在泄漏。正确地修复它需要一天左右的时间来重构一段原本可以正常工作的代码。我本可以做一些骇人听闻的事情(比如将值填充到一个全局变量中并在某个我知道它不再用于释放的地方抓住它),但这只会给下一个不得不接触代码的人带来更多的困惑。

就个人而言,我一开始不会那样编写代码,但我们大多数人并不总是在设计良好的原始代码库上工作,有时你必须务实地看待这些事情。我修复 150 字节泄漏所花费的时间可以用来进行算法改进,从而减少数兆字节的内存。

最终,我认为对于一个使用大约 1 g ram 并在专用机器上运行的应用程序泄漏 150 字节是不值得修复的,所以我写了一条评论说它被泄露了,需要更改什么才能修复它,以及为什么当时不值得。

于 2008-11-07T19:33:04.257 回答
5

这是特定领域的问题,几乎不值得回答。用你那该死的脑袋。

  • 航天飞机操作系统:不,不允许内存泄漏
  • 快速开发概念验证代码:修复所有这些内存泄漏是浪费时间。

并且有一系列中间情况。

延迟产品发布以修复除了最严重的内存泄漏之外的所有问题的机会成本($$$)通常会使任何“马虎或不专业”的感觉相形见绌。你的老板付钱给你是为了让他赚钱,而不是为了得到一种温暖、模糊的感觉。

于 2008-11-07T19:13:31.237 回答
5

您必须首先意识到感知到的内存泄漏和实际的内存泄漏之间存在很大差异。分析工具经常会报告许多红鲱鱼,并将某些东西标记为已泄漏(内存或资源,如句柄等),而实际上并没有。这通常是由于分析工具的架构造成的。例如,某些分析工具会将运行时对象报告为内存泄漏,因为它永远不会看到这些对象被释放。但是释放发生在运行时的关闭代码中,分析工具可能无法看到。

话虽如此,有时您仍然会遇到很难找到或很难修复的实际内存泄漏。所以现在问题变成了将它们留在代码中是否可以?

理想的答案是“不,永远不会”。更务实的答案可能是“不,几乎从不”。在现实生活中,您通常只有有限的资源和时间来解决以及无穷无尽的任务列表。当其中一项任务是消除内存泄漏时,收益递减法则经常发挥作用。你可以在一周内消除应用程序中 98% 的内存泄漏,但剩下的 2% 可能需要几个月的时间。在某些情况下,由于应用程序的架构,如果不对代码进行重大重构,甚至可能无法消除某些泄漏。您必须权衡消除剩余 2% 的成本和收益。

于 2008-11-07T19:15:09.190 回答
5

虽然大多数答案都集中在真正的内存泄漏上(这从来都不好,因为它们是编码草率的标志),这部分问题对我来说似乎更有趣:

如果您分配一些内存并使用它直到应用程序中的最后一行代码(例如,全局对象的解构器)怎么办?只要内存消耗不随时间增长,是否可以相信操作系统在应用程序终止时(在 Windows、Mac 和 Linux 上)为您释放内存?如果内存被连续使用直到被操作系统释放,你甚至会认为这是一个真正的内存泄漏。

如果使用了关联的内存,则无法在程序结束之前释放它。免费是由程序退出还是由操作系统完成并不重要。只要对此进行了记录,这样更改就不会引入真正的内存泄漏,并且只要图片中不涉及 C++ 析构函数或 C 清理函数。未关闭的文件可能会通过泄漏的FILE对象暴露出来,但缺少 fclose() 也可能导致缓冲区不被刷新。

因此,回到最初的情况,恕我直言,它本身完全没问题,以至于 Valgrind,最强大的检漏仪之一,只会在请求时处理此类泄漏。在 Valgrind 上,当你覆盖一个指针而不事先释放它时,它被认为是内存泄漏,因为它更有可能再次发生并导致堆无休止地增长。

然后,没有仍然可以访问的 nfreed 内存块。可以确保在出口处释放所有人,但这本身就是浪费时间。关键是他们之前是否可以被释放。在任何情况下,降低内存消耗都是有用的。

于 2009-01-12T03:23:01.987 回答
4

我同意 vfilby - 这取决于。在 Windows 中,我们将内存泄漏视为相对严重的错误。但是,它在很大程度上取决于组件。

例如,对于很少运行且运行时间有限的组件,内存泄漏并不是很严重。这些组件运行,完成它们的工作,然后退出。当他们退出时,他们的所有内存都会被隐式释放。

但是,服务或其他长期运行的组件(如 shell)中的内存泄漏非常严重。原因是这些错误会随着时间的推移“窃取”内存。恢复这种情况的唯一方法是重新启动组件。大多数人不知道如何重新启动服务或 shell——所以如果他们的系统性能受到影响,他们只会重新启动。

因此,如果您有泄漏 - 以两种方式评估其影响

  1. 为了您的软件和您的用户体验。
  2. 对系统(和用户)而言,节约使用系统资源。
  3. 修复对维护和可靠性的影响。
  4. 在其他地方引起回归的可能性。

福特克

于 2008-11-07T19:15:33.387 回答
4

即使您确定您的“已知”内存泄漏不会造成严重破坏,也不要这样做。充其量,它会为您在不同的时间和地点犯下类似且可能更严重的错误铺平道路。

对我来说,问这个问题就像在问“我可以在凌晨 3 点在没有人的情况下闯红灯吗?”。可以肯定的是,它在那个时候可能不会造成任何麻烦,但它会为您在高峰时间做同样的事情提供一个杠杆!

于 2008-11-07T20:32:54.620 回答
4

不,您不应该有操作系统会为您清理的泄漏。原因(据我所知,上面的答案中没有提到)是你永远不知道你的 main() 什么时候会被重新用作另一个程序中的函数/模块。如果您的 main() 成为其他人软件中经常调用的函数 - 该软件将出现内存泄漏,随着时间的推移会占用内存。

基辅

于 2008-11-16T07:36:03.757 回答
4

我很惊讶地看到这么多关于内存泄漏实际上是什么的错误定义。没有一个具体的定义,关于它是否是坏事的讨论将无从谈起。

正如一些评论者正确指出的那样,只有当进程分配的内存超出范围以至于进程不再能够引用或删除它时,才会发生内存泄漏。

占用越来越多内存的进程不一定会泄漏。只要它能够引用和取消分配该内存,它就会保持在进程的显式控制之下并且没有泄漏。该过程可能设计得很糟糕,尤其是在内存有限的系统环境中,但这与泄漏不同。相反,即使泄漏的内存量很小,失去范围(例如 32 字节缓冲区)仍然是泄漏。如果您认为这无关紧要,请等到有人在您的库调用中封装一个算法并调用它 10,000 次。

我认为没有任何理由允许您自己的代码泄漏,无论多么小。现代编程语言(如 C 和 C++)竭尽全力帮助程序员防止此类泄漏,而且很少有理由不采用良好的编程技术(尤其是在与特定语言设施结合使用时)来防止泄漏。

对于现有或第三方代码,您对质量的控制或进行更改的能力可能受到高度限制,根据泄漏的严重程度,您可能被迫接受或采取缓解措施,例如定期重新启动流程以减少泄漏的影响。

可能无法更改或替换现有(泄漏)代码,因此您可能必须接受它。但是,这与声明没关系是不一样的。

于 2016-04-11T09:55:26.940 回答
3

我想如果您正在编写一个旨在泄漏内存的程序(即测试内存泄漏对系统性能的影响),那很好。

于 2010-09-18T20:04:21.487 回答
2

如果它是故意的并且它不是问题,除非它有大量内存,否则它真的不是泄漏,或者可能会增长到大量内存。在程序的生命周期内不清理全局分配是相当普遍的。如果泄漏发生在服务器或长时间运行的应用程序中,并且随着时间的推移而增长,那么它就是一个问题。

于 2008-11-07T19:07:00.783 回答
2

我想你已经回答了你自己的问题。最大的缺点是它们如何干扰内存泄漏检测工具,但我认为这个缺点对于某些类型的应用程序来说是一个巨大的缺点。

我使用应该坚如磐石的遗留服务器应用程序,但它们存在泄漏,并且全局变量确实妨碍了内存检测工具。这是一件大事。

在贾里德·戴蒙德 (Jared Diamond) 的《崩溃》一书中,作者想知道那个砍倒复活节岛上最后一棵树的人在想什么,他需要这棵树才能建造一艘独木舟下岛。我想知道多年前将第一个全局变量添加到我们的代码库中的那一天。那是它应该被抓住的那一天。

于 2008-11-07T19:07:51.530 回答
2

我看到了与所有这样的场景问题相同的问题:当程序发生变化时会发生什么,突然间那个小内存泄漏被调用了一千万次,而你的程序的结尾在不同的地方,所以这很重要吗?如果它在库中,则向库维护者记录错误,不要将泄漏放入您自己的代码中。

于 2008-11-07T19:08:55.070 回答
2

我会回答不。

理论上,如果你留下一团糟,操作系统会在你之后清理(现在这很粗鲁,但由于计算机没有感觉,这可能是可以接受的)。但是您无法预测程序运行时可能发生的所有情况。因此(除非您能够对某些行为进行正式证明),从专业角度来看,创建内存泄漏是不负责任和草率的。

如果第三方组件泄漏内存,这是一个非常强烈的反对使用它的论据,不仅因为迫在眉睫的影响,还因为它表明程序员工作马虎并且这也可能影响其他指标。现在,在考虑遗留系统时,这很困难(考虑 Web 浏览组件:据我所知,它们都会泄漏内存),但这应该是常态。

于 2008-11-07T19:12:19.070 回答
2

从历史上看,在某些边缘情况下,它在某些操作系统上确实很重要。这些边缘情况可能在未来存在。

举个例子,在 Sun 3 时代的 SunOS 上,如果一个进程使用 exec(或者更传统的 fork 然后 exec),那么后续的新进程将继承与父进程相同的内存占用,并且无法收缩. 如果父进程分配了 1/2 gig 的内存并且在调用 exec 之前没有释放它,则子进程将开始使用相同的 1/2 gig(即使它没有被分配)。SunTools(他们的默认窗口系统)最好地展示了这种行为,它是一个内存猪。它产生的每个应用程序都是通过 fork/exec 创建的,并继承了 SunTools 的足迹,迅速填满了交换空间。

于 2008-11-07T19:20:07.137 回答
2

这已经被讨论得令人作呕。底线是内存泄漏是一个错误,必须修复。如果第三方库泄漏内存,人们会想知道它还有什么问题,不是吗?如果您正在制造汽车,您会使用偶尔漏油的发动机吗?毕竟,引擎是别人制造的,所以这不是你的错,你也修不了,对吧?

于 2008-11-07T22:22:18.407 回答
2

通常,独立应用程序中的内存泄漏不是致命的,因为它会在程序退出时被清除。

对于设计为不会退出的服务器程序,您会怎么做?

如果您是那种不会设计和实现正确分配和释放资源的代码的程序员,那么我不希望与您或您的代码有任何关系。如果您不想清理泄漏的内存,那么您的锁呢?你也让他们在那里闲逛吗?您是否在各个目录中留下了一些临时文件?

泄漏内存并让程序清理它?不,绝对不是。这是一个坏习惯,会导致错误、错误和更多错误。

自己打扫干净。哟妈妈不再在这里工作了。

于 2008-11-07T22:24:15.207 回答
2

作为一般规则,如果您感到无法避免的内存泄漏,那么您需要更加认真地考虑对象所有权。

但是对于您的问题,我的回答是在生产代码中,是的。在开发过程中,没有. 这可能看起来倒退,但这是我的推理:

在您描述的情况下,内存一直保留到程序结束,不释放它是完全可以的。一旦您的进程退出,操作系统无论如何都会清理。事实上,它可能会让用户体验更好:在我开发的一款游戏中,程序员认为在退出之前释放所有内存会更干净,导致程序关闭最多需要半分钟!一个刚刚调用 exit() 的快速更改反而使该进程立即消失,并将用户放回他想要的桌面。

但是,您对调试工具的看法是正确的:它们会出错,并且所有误报都可能使查找真正的内存泄漏变得很痛苦。因此,请始终编写释放内存的调试代码,并在发布时禁用它。

于 2008-11-11T19:27:17.027 回答
2

我只看到一个实际的缺点,那就是这些良性泄漏会在内存泄漏检测工具中显示为误报。

如果我理解正确,您不会显式释放内存(可以释放内存,因为您仍然有一个指针)并依赖操作系统在进程终止期间释放它。虽然这对于简单的程序来说似乎没问题,但考虑一下您的代码被移动到库中并成为某个 24/7 运行的常驻守护进程的一部分的情况。假设这个守护进程每次需要使用您的代码做一些有用的事情时都会产生一个线程,并说它每小时产生数千个线程。在这种情况下,您将得到真正的内存泄漏。

不幸的是,这种情况在现实生活中并非不可能,一致的内存管理技术可能会让您的生活更轻松。

于 2013-08-07T13:38:51.197 回答
2

是的,内存泄漏可能是两害相权取其轻。虽然正确性很重要,但在执行完全内存释放时,系统的性能或稳定性可能会受到影响,并且释放内存和销毁对象所花费的风险和时间可能不如仅仅退出进程。

一般来说,留下内存通常是不可接受的。很难理解您的代码将在其中运行的所有范围,在某些情况下,它可能导致泄漏成为灾难性的。

如果您分配一些内存并使用它直到应用程序中的最后一行代码(例如,全局对象的析构函数)怎么办?

在这种情况下,您的代码可能会被移植到更大的项目中。这可能意味着您的对象的生命周期太长(它持续整个程序,而不仅仅是需要它的实例),或者如果全局被创建和销毁,它会泄漏。

当您的应用程序终止时,是否可以信任操作系统为您释放内存?

当一个短暂的程序创建大型C++集合(例如std::map)时,每个对象至少有 2 个分配。遍历此集合以销毁对象需要 CPU 的实时时间,并且让对象泄漏并由操作系统整理,具有性能优势。计数器,是否有一些资源没有被操作系统整理(例如共享内存),并且不破坏代码中的所有对象会带来一些风险,即某些人持有这些未释放的资源。

如果第三方库将这种情况强加给您怎么办?

close首先,我会为释放资源的函数提出一个错误。关于它是否可以接受的问题是基于库提供的优势(成本、性能、可靠性)是否比使用其他库或自己编写更好。

一般来说,除非可以重新初始化库,否则我可能不会担心。

报告泄漏内存的可接受时间。

  1. 关闭期间的服务。这里需要在时间性能和正确性之间进行权衡。
  2. 无法摧毁的破碎物体。我已经能够检测到失败的对象(例如,由于捕获了异常),当我尝试销毁对象时,结果是挂起(保持锁定)。
  3. 内存检查器误报。

关闭期间的服务

如果操作系统即将关闭,所有资源都会被清理。不执行正常进程关闭的优点是用户在关闭时可以获得更快的性能。

破碎的物体

在我的过去,我们发现了一个对象(并为该团队提出了一个缺陷),如果它们在某些点崩溃,它们就会损坏,该对象中的所有后续功能都会导致挂起。

虽然忽略内存泄漏是不好的做法,但关闭我们的进程、泄漏对象及其内存比导致挂起更有效率。

泄漏检查器误报

一些泄漏检查器通过检测对象来工作,其行为方式与全局对象相同。他们有时会错过另一个全局对象有一个有效的析构函数,在他们完成后调用它会释放内存。

于 2017-08-05T10:53:21.483 回答
1

我完全同意 JohnMcG 的观点,并且只想补充一点,我自己在及时发现真正的、潜在的严重内存泄漏方面遇到了问题,只是因为已经接受了良性的内存泄漏。当这些随着时间的推移变得如此之多时,在良性疾病的洪流中发现严重的疾病变得越来越困难。

所以至少为了你的程序员同行(也为了你自己的未来),请尝试尽快淘汰他们。

于 2008-11-07T19:29:46.617 回答
1

看起来您对“内存泄漏”的定义是“我自己不清理的内存”。所有现代操作系统都会在程序退出时释放它。但是,由于这是一个 C++ 问题,您可以简单地将有问题的内存包装在适当的内存中,std::auto_ptr当它超出范围时将调用 delete。

于 2008-11-07T23:54:36.753 回答
1

这实际上取决于创建内存泄漏的对象的使用情况。如果您在使用该对象的应用程序的生命周期中多次创建该对象,那么使用这种方式是不好的。因为会有太多的内存泄漏。另一方面,如果我们有一个对象实例而不消耗内存并且仅少量泄漏,那么这不是问题。

当应用程序运行时泄漏增加时,内存泄漏是一个问题。

于 2008-11-12T15:48:24.143 回答
1

当应用程序关闭时,可以说最好不要释放内存。

理论上,操作系统应该释放应用程序使用的资源,但总有一些资源是这个规则的例外。所以要小心。

退出应用程序的好处:

  1. 操作系统会释放一个块而不是许多小块。这意味着关机要快得多。特别是在 Windows 上,它的内存管理速度很慢。

刚退出的坏处实际上有两点:

  1. 很容易忘记释放操作系统不跟踪的资源,或者操作系统可能会在释放时稍等片刻。一个例子是 TCP 套接字。
  2. 内存跟踪软件会将退出时未释放的所有内容报告为泄漏。

因此,您可能希望有两种关闭模式,一种对最终用户来说快速而肮脏,另一种对开发人员来说缓慢而彻底。只要确保测试两者:)

于 2008-11-19T12:22:53.363 回答
1

我在高中上过一门 C 课,老师说当你 malloc 时一定要释放。

但是当我参加另一门课程大学时,教授说不为只运行一秒钟的小程序免费是可以的。所以我想它不会伤害你的程序,但释放强大、健康的代码是一种很好的做法。

于 2010-05-01T19:28:35.177 回答
1

这里有一些很好的答案。为了向这个问题添加另一个观点,我将解决内存泄漏不仅可以接受而且是可取的情况:在 Windows 驱动程序环境中,开发人员提供了一组由操作系统在需要时运行的回调。其中一个回调是“关闭”回调,它在系统关闭/重新启动之前运行。与标准情况不同,不仅不需要释放内存(系统将立即关闭),甚至不鼓励 - 尽可能快地关闭并防止内存管理的开销。

于 2018-05-24T01:35:56.857 回答
0

只要您的内存利用率不随时间增加,这取决于。如果您在服务器软件中进行大量复杂的同步,例如启动阻塞系统调用的后台线程,那么完全关闭可能太复杂而无法证明是合理的。在这种情况下,替代方案可能是:

  1. 您的库在进程退出之前不会清理其内存。
  2. 您编写了额外的 500 行代码,并向您的类添加了另一个互斥锁和条件变量,以便它可以从您的测试中干净地关闭 - 但此代码从未在生产中使用,服务器只会因崩溃而终止。
于 2008-11-16T07:08:03.063 回答
0

仅在一种情况下:由于不可恢复的错误,程序将自行射击。

于 2009-01-07T06:58:21.220 回答
0

最佳实践是始终释放您分配的内容,尤其是在编写旨在在系统的整个正常运行时间内运行的内容时,即使在退出之前进行清理时也是如此。

这是一个非常简单的规则.. 以没有泄漏为目的的编程使得新的泄漏很容易被发现。你会卖给别人一辆你知道它每次熄火都会在地上溅出汽油的汽车吗?:)

清理函数中的一些 if () free() 调用很便宜,为什么不使用它们呢?

于 2009-01-07T07:21:23.107 回答
0

如果您一直使用它直到main().

事实上,在进程关闭时释放对象绝对是您可以做的最糟糕的事情......操作系统必须在您创建的每个页面中重新分页。关闭文件句柄,数据库连接,当然,但释放内存只是愚蠢的。

于 2009-02-27T05:43:47.660 回答
0

如果您的代码有任何内存泄漏,甚至是已知的“可接受的”泄漏,那么您将有一段恼人的时间使用任何内存泄漏工具来查找您的“真实”泄漏。就像留下“可接受的”编译器警告会使查找新的“真实”警告变得更加困难。

于 2009-02-27T06:00:24.140 回答
0

不,它们不好,但是我已经实现了一些分配器、内存转储器和泄漏检测器,并且发现作为一种实用的事情,允许将这样的分配标记为“就泄漏报告令人担忧” ...

这有助于使泄漏报告更有用......并且不会被“程序退出未释放的静态范围内的动态分配”所挤满

于 2009-03-23T01:48:21.923 回答
0

可能会令人毛骨悚然:如果您的应用程序在 UNIX 上运行并且可能变成僵尸怎么办?在这种情况下,内存不会被操作系统回收。所以我说你真的应该在程序退出之前取消分配内存。

于 2009-08-14T00:43:30.890 回答
0

在程序的最后一行省略释放内存是完全可以接受的,因为释放它不会对任何事情产生影响,因为程序不再需要内存。

于 2009-11-01T11:53:53.343 回答
0

我相信如果您有一个程序可以运行几秒钟然后退出并且仅供个人使用,那是可以的。程序结束后,任何内存泄漏都会被清除。

当您有一个运行时间很长的程序并且用户依赖它时,问题就来了。此外,让程序中存在内存泄漏也是一种不好的编码习惯,尤其是在工作中,如果有一天它们可能将该代码变成其他东西。

总而言之,消除内存泄漏更好。

于 2010-03-07T19:13:34.670 回答
0

前段时间我会说是的,在你的程序中允许一些内存泄漏有时是可以接受的(它仍处于快速原型设计中),但现在已经取得了 5 或 6 倍的经验,即使跟踪最小的泄漏也会显示一些非常严重的问题功能错误。当数据实体的生命周期不为人所知时,程序中就会发生泄漏,这表明缺乏分析。所以总而言之,了解程序中发生了什么总是一个好主意。

于 2010-06-29T20:05:43.970 回答
0

考虑一下应用程序后来被另一个应用程序使用的情况,有可能在单独的窗口中打开其中的几个或一个接一个地做某事。如果它不是运行一个进程,而是作为一个库运行,那么调用程序会泄漏内存,因为你认为你冷跳过了内存清理。

使用某种自动为您执行此操作的智能指针(例如来自 Boost 库的 scoped_ptr)

于 2010-07-27T12:07:51.563 回答
0

规则很简单:如果你用完一些内存就清理它。有时即使我们稍后需要一些实例,但我们注意到我们大量使用内存,因此它会由于交换到磁盘而影响性能,我们可以将数据存储到磁盘中的文件并在重新加载它们之后,有时这种技术会优化你的程序.

于 2011-01-26T15:05:23.697 回答