内存泄漏是否正常?

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

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

如果第三方图书馆强迫你这样做,会怎么样? 拒绝使用第三方图书馆,不pipe它有多么伟大?

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

没有。

作为专业人士,我们不应该问自己的问题是:“这样做可以吗?” 而是“有没有这样做的理由?” 而“狩猎内存泄漏是一个痛苦”不是一个好的理由。

我喜欢保持简单。 简单的规则是我的程序应该没有内存泄漏。

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

这与编译器警告类似 – 警告是否会对我的特定应用程序是致命的? 也许不会。

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

为了让事情变得极端,外科医生是否可以将一些操作设备留在患者体内?

尽pipe可能会出现这样的情况:移除该设备的成本/风险超过了成本/风险,并且可能在无害的情况下,如果我在SurgeonOverflow.com上看到这个问题看到“不”的答案,就会严重破坏我对医学界的信心。

如果第三方图书馆强迫我这样做,会令我严重怀疑有关图书馆的整体质素。 就好像我testing驾驶一辆汽车,发现在一个杯架上有一些松动的垫圈和螺母 – 它本身可能不是什么大不了的事情,但它描绘了对质量的承诺,所以我会考虑替代品。

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

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

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

请注意,while(1)循环中每次分配1024(+开销)字节,并将新地址分配给vp; 没有剩下的指针指向以前的malloc'ed块。 这个程序保证运行,直到堆耗尽,没有办法恢复任何malloc'ed内存。 记忆从堆中“泄漏”出来,永远不会再被看到。

你所描述的,听起来像

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

你分配内存,使用它,直到程序终止。 这不是内存泄漏; 它不影响程序,当程序终止时,所有的内存将被自动清除。

一般来说,你应该避免内存泄漏。 首先,因为像你身上的高空,在机库回油,泄漏的,不能回收的记忆是无用的; 其次,在开始的时候,正确地编写代码要容易得多,而不是泄漏内存,而不是稍后发现内存泄漏。

理论上不行,实际上取决于

这实际上取决于程序运行的数据量,程序运行的频率以及是否运行频繁。

如果我有一个快速的程序,读取less量的数据进行计算并退出,一个小的内存泄漏将永远不会被注意到。 由于程序运行时间不长,只能使用less量的内存,程序存在时泄漏量会很小,并且会被释放。

另一方面,如果我有一个可以处理数百万条logging并且运行很长时间的程序,那么只要有足够的时间,一个小小的内存泄漏就会使机器停下来。

至于有泄漏的第三方库,如果它们引起问题,可以修复库或find更好的select。 如果不会造成问题,那真的很重要吗?

很多人似乎都觉得,一旦你释放了内存,它就会立即返回到操作系统,并被其他程序使用。

这是不正确的。 操作系统通常以4KiB页面pipe理内存。 malloc和其他types的内存pipe理从操作系统获取页面,并按照他们认为合适的方式进行分区pipe理。 free()很有可能不会返回页面到操作系统,假设你的程序将malloc更多的内存。

我并不是说free()永远不会将内存返回给操作系统。 它可能发生,特别是如果你释放大量的内存。 但是没有保证。

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

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

关于为什么在终止之前释放内存的更多细节,请参阅此注释 。

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

所以,要说服人们,我会举一个例子,free()没有什么好处。 为了使math易于遵循,我将假装操作系统pipe理4000字节页面的内存。

假设你分配了一万个100字节块(为了简单起见,我将忽略pipe理这些分配所需的额外内存)。 这消耗1MB,或250页。 如果你随机抽取了9000个这样的区块,你只剩下1000个区块 – 但是它们分散在各处。 统计上,约5页将是空的。 另外245个每个都至less有一个分配块。 这相当于980KB的内存,不可能被操作系统回收 – 即使你现在只分配了100KB!

另一方面,你现在可以malloc()9000块,而不增加你的程序捆绑的内存量。

即使free() 技术上可以将内存返回到操作系统,它可能不会这样做。 free()需要在快速操作和节省内存之间达到平衡。 而且,一个已经分配了大量内存然后释放的程序很可能会再次这样做。 一个Web服务器在请求后需要请求处理请求 – 这是有意义的,保持一些“松散”的内存可用,所以你不需要一直向操作系统请求内存。

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

这真的取决于应用程序以及如何运行。 在需要运行数周的应用程序中不断发生的泄漏必须得到处理,但是一个计算结果的小工具不应该是一个问题。

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

我相信答案是否定的,永远不要让内存泄漏,我有几个我没有看到明确说明的原因。 这里有很多技术性的答案,但我认为真正的答案取决于更多的社会/人的原因。

(首先,请注意,正如其他人提到的那样,一个真正的泄漏就是当程序在任何时候丢失了已经分配的内存资源的轨迹,在C中,当你使用malloc()指向一个指针并让该指针离开scope没有做一个free()第一)。

你在这里决定的重要关键是习惯。 当你用一个使用指针的语言进行编码时,你将会使用很多指针。 指针是危险的; 它们是将所有严重问题添加到代码中的最简单方法。

当你编码的时候,有时你会在球上,有时你会疲倦,生气或担心。 在那些有点分心的时候,你在自动驾驶仪上编码更多。 自动驾驶的效果不能区分一个大的项目中的一次性代码和模块。 在那段时间里,你build立的习惯将会以你的代码为基础。

所以不可以,因为同样的原因,不要让内存泄漏,即使你是目前唯一的汽车,你仍然应该检查你的盲点。 在你活跃的大脑分散注意力的时候,良好的习惯可以帮助你避免灾难性的失误。

除了“习惯”问题,指针是复杂的,往往需要大量的脑力来精神追踪。 当涉及到指针的使用时,最好不要“浑水摸鱼”,特别是当你不熟悉编程时。

还有更多的社交方面。 通过正确使用malloc()free() ,任何查看代码的人都会放心; 你正在pipe理你的资源。 如果你不这样做,他们会马上怀疑有问题。

也许你已经发现内存泄漏并不会损害这方面的任何内容,但是当你阅读那段代码时,你的代码的每个维护者都必须在他的脑海中解决这个问题。 通过使用free()您甚至不必考虑问题。

最后,编程将一个过程的心理模型写成一个明确的语言,以便一个人和一台计算机可以完全理解这个过程。 良好的编程实践的一个重要组成部分是从来没有引入不必要的模糊。

智能编程是灵活的和通用的。 糟糕的编程是不明确的。

我打算给出一个不受欢迎但实际的答案: 释放内存总是错误的,除非这样做会减less程序的内存使用量 。 例如,一个程序,使一个单一的分配或一系列的分配加载数据集,它将在整个生命周期中使用,不需要释放任何东西。 在更常见的情况下,对于需要dynamic内存的大型程序(想一下networking浏览器),应该尽快释放你不再使用的内存(例如closures标签/文档等)。但是当用户select点击“退出”时,没有任何理由去释放任何东西,这样做实际上对用户体验是有害的。

为什么? 释放内存需要触摸内存。 即使您的系统的malloc实现不会将元数据存储在已分配的内存块旁边,您也可能需要逐步recursion结构来查找所有需要释放的指针。

现在,假设你的程序已经处理了大量的数据,但是一段时间没有触及大部分数据(再次,Web浏览器就是一个很好的例子)。 如果用户正在运行很多应用程序,那么这些数据的很大一部分可能已经交换到了磁盘。 如果你只是退出(0)或从主返回,它立即退出。 伟大的用户体验。 如果你试图释放所有东西的麻烦,你可能花费5秒钟或更长的时间把所有的数据交换回去,只是在这之后马上扔掉。 浪费用户的时间。 浪费笔记本电脑的电池寿命。 浪费硬盘上的磨损。

这不仅仅是理论上的。 每当我发现自己有太多的应用程序加载和磁盘开始颠簸,我甚至不考虑点击“退出”。 我尽可能快地到达terminal,然后inputkillall -9 …因为我知道“退出”只会使情况变得更糟。

我认为在你的情况下,答案可能是没关系。 但是你肯定需要logging内存泄漏是一个有意识的决定。 你不希望维护程序员来一个function,把你的代码放在一个函数里,并且把它称为一百万次。 所以,如果你做出泄漏的决定,那么你需要将它logging下来(大字母),以便将来可能要在这个程序上工作的人。

如果这是第三方图书馆,您可能会被困。 但肯定是文件,这种泄漏发生。

但基本上,如果内存泄漏是像512 KB缓冲区或其他什么的已知数量,那么这是一个没有问题。 如果内存泄漏持续增长,就像每次调用库调用时一样,内存增加了512KB,并且没有被释放,那么您可能会遇到问题。 如果您logging并控制呼叫的执行次数,则可能是可pipe理的。 但是,你真的需要文档,因为虽然512不是很多,但是超过一百万个电话的512个是很多的。

你也需要检查你的操作系统文件。 如果这是一个embedded式设备,则可能有操作系统不能释放退出程序的所有内存。 我不确定,也许这不是真的。 但值得一看。

我敢肯定,有人可以想出一个理由说是的,但它不会是我。 我没有说不,而是说这不应该是一个“是/否”的问题。 有办法来pipe理或包含内存泄漏,许多系统都有。

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

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

一方面,我可以指望一段时间以来我看到的“良性”泄漏的数量。

所以答案是非常合格的。

一个例子。 如果你有一个单独的资源,需要一个缓冲区来存储一个循环队列或者deque,但是不知道缓冲区需要多大,并且不能承受locking或每个读取器的开销,那么分配一个指数倍增的缓冲区,但是没有释放旧的会泄漏每队列/ deque有限的内存量。 这样做的好处是可以显着加速每次访问,并且可以改变多处理器解决scheme的渐近趋势,从不冒险争夺locking。

我已经看到这种方法对于具有非常明确固定计数的事物(例如,per-CPU工作窃取deques)以及用于在Hans中保存singleton /proc/self/maps状态的缓冲区的程度要低得多Boehm用于C / C ++的保守垃圾回收器,用于检测根集合等。

虽然在技术上是泄漏,但是这两种情况都是有限的,并且在可循环的工作中窃取了deque情况下,有一个巨大的性能胜利,换取队列内存使用的有限因素2。

如果你在你的程序开始处分配了一堆堆,而你在退出的时候没有释放它,这本身不是内存泄漏。 内存泄漏是当你的程序在一段代码中循环时,代码分配堆,然后“丢失”它而不释放它。

事实上,在退出之前,不需要拨打免费()或删除权限。 当进程退出时,所有的内存都被操作系统回收(POSIX的情况当然是这样,在其他操作系统 – 尤其是embedded式操作系统上 – YMMV)。

在退出的时候,我唯一要提醒的是,如果你重构了你的程序,例如,它变成了一个等待input的服务,不pipe你的程序做了什么,然后循环等待另一个服务调用,那么你编码可以变成一个内存泄漏。

这是如此的领域特定,它几乎不值得回答。 使用你的头脑。

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

并有一系列的中间情况。

延迟产品发布来修复除最糟糕的内存泄漏之外的机会成本($$$)通常会使任何“马虎或不专业”的感觉都变得渺茫。 你的老板支付你让他赚钱,而不是得到一个温暖,模糊的感觉。

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

我正在分析一个项目,做一些工作,并修复了很多错误。 在我追踪的应用程序初始化过程中发生泄漏,并且完全理解。 正确地解决这个问题需要一天左右的时间来重构一段其他的function性代码。 我可以做一些事情(比如把价值塞进一个全球化的东西,并且抓住一些我知道它不再被用来释放的东西),但是这会给下一个不得不触摸代码的人造成更多的困惑。

就我个人而言,我不会那么写代码,但是我们大多数人不会总是在原始devise好的代码库上工作,有时你必须务实地看待这些事情。 我需要花费的时间来解决这个150字节的泄漏,而不是花费在algorithm上的改进,从而减less了几十亿的内存。

最终,我决定泄漏150个字节的应用程序,用在一个ram的演出,并运行在一个专用的机器上是不值得修复,所以我写了一个评论说,这是泄漏,什么需要改变,以便修复它,为什么当时不值得。

虽然大多数答案都集中在真正的内存泄漏问题上,但是这个问题的一部分似乎更令我感兴趣:

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

如果使用关联的内存,则在程序结束之前不能释放它。 无论是由程序退出还是由操作系统完成,都无关紧要。 只要这是有logging的,这样的变化不会引入真正的内存泄漏,只要没有涉及C ++析构函数或C清理函数的图片。 一个未closures的文件可能通过泄露的FILE对象显示,但是丢失的fclose()也可能导致缓冲区不被刷新。

所以,回到原来的情况,恕我直言,它本身就非常好,Valgrind是最强大的检漏仪之一,只有在有要求的情况下才能处理这种泄漏。 在Valgrind中,当你事先没有释放指针而覆盖指针时,它会被认为是内存泄漏,因为它更可能再次发生并导致堆栈无限增长。

然后,没有非法的内存块仍然可以到达。 人们可以确保在出口处释放所有这些,但这本身就是浪费时间。 问题是如果他们以前可以被释放的话。 降低内存消耗在任何情况下都是有用的。

你必须首先意识到在感知的内存泄漏和实际的内存泄漏之间有很大的区别。 非常频繁的分析工具会报告许多红鲱鱼,并将其标记为已被泄漏(内存或资源,如手柄等),实际上不是。 通常这是由于分析工具的体系结构。 例如,某些分析工具会将运行时对象报告为内存泄漏,因为它从来没有看到这些对象被释放。 但解除分配发生在运行时的closures代码中,分析工具可能无法看到。

有了这个说法,仍然会有些时候你会发现实际的内存泄漏,要么很难find,要么很难修复。 所以现在的问题是,把它们留在代码中是否可以?

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

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

对我来说,问这个问题就像是问:“当凌晨三点,当我们周围没有人时,我可以打破红灯吗?” 当然,那个时候可能不会有什么麻烦,但是它提供了一个杠杆让你在高峰时间做同样的事情!

我很惊讶地发现有太多内存泄漏的错误定义。 没有一个具体的定义,讨论这个坏事是否会失败。

正如一些评论者正确地指出的,内存泄漏只发生在进程分配的内存超出范围的情况下,进程不能再引用或删除它。

抓取越来越多内存的过程并不一定是泄漏。 只要能够引用和释放内存,那么它仍然处于进程的明确控制之下,并没有泄漏。 这个过程可能会被devise的很糟糕,特别是在内存有限的系统的情况下,但这不像泄漏一样。 相反,即使泄漏的内存量很小,丢失32字节缓冲区的范围仍然是泄漏。 If you think this is insignificant, wait until someone wraps an algorithm around your library call and calls it 10,000 times.

I see no reason whatsoever to allow leaks in your own code, however small. Modern programming languages such as C and C++ go to great lengths to help programmers prevent such leaks and there is rarely a good argument not to adopt good programming techniques – especially when coupled with specific language facilities – to prevent leaks.

As regards existing or third party code, where your control over quality or ability to make a change may be highly limited, depending on the severity of the leak, you may be forced to accept or take mitigating action such as restarting your process regularly to reduce the effect of the leak.

It may not be possible to change or replace the existing (leaking) code, and therefore you may be bound to accept it. However, this is not the same as declaring that it's OK.

I think you've answered your own question. The biggest drawback is how they interfere with the memory leak detection tools, but I think that drawback is a HUGE drawback for certain types of applications.

I work with legacy server applications that are supposed to be rock solid but they have leaks and the globals DO get in the way of the memory detection tools. It's a big deal.

In the book "Collapse" by Jared Diamond, the author wonders about what the guy was thinking who cut down the last tree on Easter Island, the tree he would have needed in order to build a canoe to get off the island. I wonder about the day many years ago when that first global was added to our codebase. THAT was the day it should have been caught.

I see the same problem as all scenario questions like this: What happens when the program changes, and suddenly that little memory leak is being called ten million times and the end of your program is in a different place so it does matter? If it's in a library then log a bug with the library maintainers, don't put a leak into your own code.

I'll answer no.

In theory, the operating system will clean up after you if you leave a mess (now that's just rude, but since computers don't have feelings it might be acceptable). But you can't anticipate every possible situation that might occur when your program is run. Therefore (unless you are able to conduct a formal proof of some behaviour), creating memory leaks is just irresponsible and sloppy from a professional point of view.

If a third-party component leaks memory, this is a very strong argument against using it, not only because of the imminent effect but also because it shows that the programmers work sloppily and that this might also impact other metrics. Now, when considering legacy systems this is difficult (consider web browsing components: to my knowledge, they all leak memory) but it should be the norm.

Historically, it did matter on some operating systems under some edge cases. These edge cases could exist in the future.

Here's an example, on SunOS in the Sun 3 era, there was an issue if a process used exec (or more traditionally fork and then exec), the subsequent new process would inherit the same memory footprint as the parent and it could not be shrunk. If a parent process allocated 1/2 gig of memory and didn't free it before calling exec, the child process would start using that same 1/2 gig (even though it wasn't allocated). This behavior was best exhibited by SunTools (their default windowing system), which was a memory hog. Every app that it spawned was created via fork/exec and inherited SunTools footprint, quickly filling up swap space.

This was already discussed ad nauseam . Bottom line is that a memory leak is a bug and must be fixed. If a third party library leaks memory, it makes one wonder what else is wrong with it, no? If you were building a car, would you use an engine that is occasionally leaking oil? After all, somebody else made the engine, so it's not your fault and you can't fix it, right?

Generally a memory leak in a stand alone application is not fatal, as it gets cleaned up when the program exits.

What do you do for Server programs that are designed so they don't exit?

If you are the kind of programmer that does not design and implement code where the resources are allocated and released correctly, then I don't want anything to do with you or your code. If you don't care to clean up your leaked memory, what about your locks? Do you leave them hanging out there too? Do you leave little turds of temporary files laying around in various directories?

Leak that memory and let the program clean it up? No. Absolutely not. It's a bad habit, that leads to bugs, bugs, and more bugs.

Clean up after yourself. Yo momma don't work here no more.

As a general rule, if you've got memory leaks that you feel you can't avoid, then you need to think harder about object ownership.

But to your question, my answer in a nutshell is In production code, yes. During development, no . This might seem backwards, but here's my reasoning:

In the situation you describe, where the memory is held until the end of the program, it's perfectly okay to not release it. Once your process exits, the OS will clean up anyway. In fact, it might make the user's experience better: In a game I've worked on, the programmers thought it would be cleaner to free all the memory before exiting, causing the shutdown of the program to take up to half a minute! A quick change that just called exit() instead made the process disappear immediately, and put the user back to the desktop where he wanted to be.

However, you're right about the debugging tools: They'll throw a fit, and all the false positives might make finding your real memory leaks a pain. And because of that, always write debugging code that frees the memory, and disable it when you ship.

No, you should not have leaks that the OS will clean for you. The reason (not mentioned in the answers above as far as I could check) is that you never know when your main() will be re-used as a function/module in another program . If your main() gets to be a frequently-called function in another persons' software – this software will have a memory leak that eats memory over time.

KIV

I agree with vfilby – it depends. In Windows, we treat memory leaks as relatively serous bugs. But, it very much depends on the component.

For example, memory leaks are not very serious for components that run rarely, and for limited periods of time. These components run, do theire work, then exit. When they exit all their memory is freed implicitly.

However, memory leaks in services or other long run components (like the shell) are very serious. The reason is that these bugs 'steal' memory over time. The only way to recover this is to restart the components. Most people don't know how to restart a service or the shell – so if their system performance suffers, they just reboot.

So, if you have a leak – evaluate its impact two ways

  1. To your software and your user's experience.
  2. To the system (and the user) in terms of being frugal with system resources.
  3. Impact of the fix on maintenance and reliability.
  4. Likelihood of causing a regression somewhere else.

Foredecker

I guess it's fine if you're writing a program meant to leak memory (ie to test the impact of memory leaks on system performance).

I only see one practical disadvantage, and that is that these benign leaks will show up with memory leak detection tools as false positives.

If I understood correctly, you don't explicitly free memory (which can be freed because you still have a pointer) and rely on OS to free it during process termination. Though this may seem okay for simple program, consider the situation where your code is moved into a library and becomes a part of some resident daemon process running 24/7. Say this daemon spawns a thread each time it needs to do something useful using your code and say it spawns thousands of threads every hour. In this case you will get real memory leak.

Unfortunately, this situation is not unlikely in the real life and consistent memory management techniques may make your life easier.