内存泄漏可以吗?[关闭]


231

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

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

如果第三方图书馆将这种情况强加给您怎么办?不管它有多大,都会拒绝使用该第三方库吗?

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


50
如果内存消耗不会随着时间的推移而增长,那不是泄漏。
mpez0 2010年

4
大多数应用程序(包括所有.NET程序)至少具有一些缓冲区,这些缓冲区仅被分配一次,并且从未显式释放。因此mpez0的定义更加有用。
Ben Voigt 2010年

2
是的,如果您有无限的记忆。
用户

“良性”泄漏(如果存在这种情况)不是误报-这是一种非常正确地检测到的泄漏。泄漏检测,甚至您个人都不希望修复的泄漏,也是泄漏检测器存在的全部原因。
cHao 2013年

1
@ mpez0“如果内存消耗不会随着时间增长,那不是泄漏”?那不是内存泄漏的定义。泄漏是已经泄漏的内存,这意味着它没有被释放,您也不再对其进行引用,因此您不可能再次释放它。它是否增长无关紧要。
梅基'18

Answers:


329

没有。

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

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

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

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

但这最终是专业纪律问题。容忍编译器警告和容忍内存泄漏是一个坏习惯,最终将使我陷入困境。

为了达到极限,外科医生将某些操作设备留在患者体内是否可以接受?

尽管有可能会出现这样的情况:拆卸该设备的成本/风险超过了放置该设备的成本/风险,并且在某些情况下该设备无害,如果我在SurgeonOverflow.com上看到此问题,并且看到除了“否”以外的任何答案,它将严重损害我对医学界的信心。

如果第三方图书馆将这种情况强加给我,那将使我严重怀疑所涉图书馆的整体质量。好像我试驾了一辆汽车,发现其中一个杯托中有几个松动的垫圈和螺母-本身虽然不是什么大问题,但它却说明缺乏对质量的承诺,因此我会考虑其他选择。


57
同时为真和非真。最终,我们大多数人都是工资的奴隶,对工艺的任何渴望都必须使企业的需求退居二线。如果该第三方图书馆存在泄漏并节省了2个星期的工作,则可能会有使用它的商业案例
Cervo

3
无论如何,如果需要的话,我仍然会使用该库,并且没有其他合适的选择,但是我会向维护人员记录错误。
偷渡

7
虽然我个人会给出完全相同的答案,但有些程序根本无法释放内存。原因是它们是a)打算在释放内存的OS上运行,并且b)设计为不长时间运行。程序确实有很少的限制,但是我认为这是完全有效的。

2
要增加一些进行早期检查的原因:当调试工具充斥着“良性”漏洞时,您将如何找到“真正的”漏洞?如果添加批处理功能,突然您的每小时泄漏1K变成每秒1K?
peterchen

5
嗯是“不泄漏内存”“完美”吗?
JohnMcG 2010年

80

除非“使用”的内存量持续增长,否则我认为这不是内存泄漏。拥有一些未释放的内存(虽然不理想)不是大问题,除非所需的内存量不断增长。


12
从技术上讲,泄漏是分配的内存,丢失的所有引用都将丢失。最后不取消分配内存只是懒惰。
马丁·约克

17
如果您有4 GB的1次内存泄漏,那就是一个问题。
John Dibling

21
不管它是否在增长。如果分配了内存,其他程序将无法使用该内存。
比尔蜥蜴

8
>如果已分配内存,其他程序将无法使用该内存。好的,操作系统始终可以将您的内存交换到磁盘,并允许其他应用程序使用您没有利用的RAM。
Max Lybbert

4
如果程序寿命很短,则泄漏可能不是很严重。另外,虽然不理想,但分页并不像这里的某些代码那样昂贵,因为该程序对内存不感兴趣(因此不会一直交换)–除非您当然有一个GC ...
Arafangion

79

首先,让我们正确定义。内存泄漏是指动态分配内存(例如使用)时malloc(),所有对该内存的引用都将丢失而没有相应的可用空间。一种简单的制作方法是这样的:

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

请注意,每次绕while(1)循环时,都会分配1024(+ overhead)字节,并将新地址分配给vp;。没有剩余的指针指向先前的malloc块。保证该程序可以运行到堆用完为止,并且无法恢复任何已分配的内存。内存正在“泄漏”出来,再也不会被看到。

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

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

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

通常,您应该避免内存泄漏。首先,因为就像您上方的高度并向机库加油一样,已经泄漏且无法恢复的内存是无用的;其次,比起以后发现内存泄漏,一开始正确进行编码(不泄漏内存)要容易得多。


现在考虑几十个这种分配。现在考虑必须将“主体”移动到多次调用的例程中。请享用。-我同意这种情况下并没有太大的问题,但情况会发生变化。就像他们所说的那样,始终编写代码,好像要维护它的人知道您的住所。
peterchen

2
好吧,关键是不会“泄漏”已分配并保留直到程序调用_exit()的内存。
查理·马丁

1
这是内存泄漏,可能会损害您的程序。由于此过程,将来的分配可能会失败,因为我确定您正在检查malloc在任何地方都返回非零。通过过度使用记忆,例如在记忆力不足的嵌入式情况下,这可能是生与死之间的差异。
MikeJ 2010年

10
迈克,那不是真的。在兼容的C环境中,结尾main释放所有进程资源。在您描述的嵌入式环境中,您可能会看到这种情况,但没有主电源。现在,我承认可能存在错误的嵌入式环境,但对于这种环境,这不是真的,但是后来我看到了有缺陷的环境也无法正确应对+ =。
查理·马丁

3
是的,您现在发现,如果您malloc的内存过多,那将是一件坏事。它仍然不是泄漏。这不是一个泄漏之前,除非它是mallocd内存到的基准丢失。
查理·马丁

39

从理论上讲,实际上,这取决于

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

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

另一方面,如果我有一个可以处理数百万条记录并长时间运行的程序,那么在足够的时间内,少量的内存泄漏可能会使计算机瘫痪。

对于存在泄漏的第三方库,如果它们引起问题,请修复该库或找到更好的替代方法。如果没有问题,那真的重要吗?


我不知道您是否阅读了我的全部问题。我的意思是,直到应用程序结束时才使用内存。它不会随着时间增长。唯一的不,是没有通话自由/删除权。
2008年

2
那么这实际上不是内存泄漏。内存泄漏是少量未使用但未释放的内存,随着时间的流逝,此数量会越来越大。您在说的是一个内存滴。除非您的液滴很大,否则不要担心。
vfilby

“如果没有引起问题,那真的有关系吗?” 不,这没关系。我希望更多的人能得到这一点,而不是信奉宗教。
08年

2
@John:这通常不是懒惰的开发人员的问题,而是软件的演进问题。我们都犯错,臭虫是我们的交易;我们让他们修复它们,这就是我们要做的。在前期成本和长期维护之间始终保持平衡,这种平衡从来都不是那么简单。
vfilby

1
约翰,我100%同意您的意见。Imbum问题几乎是,“您接受多少”。马虎马虎..那我在显示器后面放一只虾怎么样。臭是臭。每次我们陷入困境,我们的行业都会陷入困境。如果您知道有泄漏并且是造成泄漏的,则应该修复它。
baash05

37

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

这不是真的 操作系统通常在4KiB页中管理内存。malloc以及其他类型的内存管理可从OS获取页面并对其进行适当的子管理。假设您的程序以后会分配更多的内存,free()则很有可能不会将页面返回到操作系统。

我并不是说free()永远不会将内存返回给操作系统。可能会发生这种情况,尤其是在释放大量内存的情况下。但是没有保证。

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

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

另请参阅此注释,以了解有关为何在终止之前释放内存不好的更多细节。

评论者似乎并不了解调用free()不会自动允许其他程序使用释放的内存。但这就是答案的重点!

因此,为了说服人们,我将演示一个示例,其中free()的作用很小。为了使数学更容易理解,我将假设操作系统以4000字节的页面管理内存。

假设您分配了一万个100字节的块(为简单起见,我将忽略管理这些分配所需的额外内存)。这将占用1MB或250页。如果您随后随机释放9000个这些块,则只剩下1000个块-但它们分散在各处。据统计,大约有5页是空白的。另一个245将各自具有至少一个分配的块。总计980KB的内存,即使您现在只分配了100KB,也无法由操作系统回收!

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

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


1
如果其他程序不需要您的程序占用的内存,该怎么办,因此即使您可能不再需要mallocs了,free()未使用的内存空间也可以:)
MN

2
你完全错过了我的观点。当您释放内存时,其他程序将无法使用它!(有时它们可​​以,尤其是如果您释放大量内存。但是通常,它们不能!)我将编辑我的帖子以使内容更清楚。
Artelius

27

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

它实际上取决于应用程序及其运行方式。必须处理需要运行数周的应用程序中不断发生的泄漏,但是使用一个小的工具来计算结果而又不需要太多的内存就不会成为问题。

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


关于脚本语言:Python使用引用计数,但是具有GC来释放循环引用。在其他语言中,程序员通常经常完全避免显式的循环引用,这会带来其他问题。
Blaisorblade

早期版本的PHP不会释放内存,它们只是从头到尾地在内存中运行-在通常的0.1秒执行时间后,脚本将退出,所有内存将被回收。
Arafangion

19

我相信答案是否定的,永远不会允许内存泄漏,并且我有一些我未明确指出的原因。这里有很好的技术答案,但我认为真正的答案取决于更多的社会/人为原因。

(首先,请注意,正如其他人提到的那样,真正的泄漏是程序在任何时候都失去对已分配的内存资源的跟踪。在C中,当您malloc()指向一个指针并让该指针离开范围而不执行操作时,就会发生这种情况。free()第一。)

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

当您编写代码时,有时您会陷入困境,有时会感到疲倦,发疯或担心。在那些分神的时期,您将在自动驾驶仪上进行更多编码。自动驾驶功能无法将一次性代码与大型项目中的模块区分开。在这段时间里,您建立的习惯将最终出现在您的代码库中。

因此,不行,切勿因为相同的原因而导致内存泄漏,因为即使您目前是道路上唯一的汽车,在更改车道时仍应检查盲点。在活跃的大脑分心的时候,良好的习惯可以使您免于灾难性的失误。

除了“习惯”问题外,指针还很复杂,通常需要大量的脑力来进行心理跟踪。在使用指针时,最好不要“浑水”,特别是在您刚接触编程时。

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

也许您已经得出结论,在这种情况下内存泄漏不会对您造成任何伤害,但是您的代码的每个维护者在阅读这段代码时也都必须在脑海中解决这个问题。通过使用,free()您甚至无需考虑该问题。

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

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


我喜欢这个习惯。我也同意。如果看到内存泄漏,我总是想知道编码器切到了哪个角落。特别是如果它很明显
baash05

到目前为止,这是最好的答案。我从事C ++编程已有5年了,从未写过一次内存泄漏。原因是我不编写容易泄漏内存的代码。好的C ++设计很少使用new,因此可以立即消除大多数内存泄漏。仅当您绝对必须使用时new。结果new必须立即放入智能指针中。如果遵循这两个规则,您将永远不会泄漏内存(除非存在库中的错误)。剩下的唯一情况是shared_ptr循环,在这种情况下,您必须知道要使用weak_ptr
大卫·斯通

15

我认为根据您的情况,答案可能是还可以。但是,您绝对需要证明内存泄漏是一个有意识的决定。您不希望维护程序员出现,将您的代码拍打到一个函数中,然后调用一百万次。因此,如果您确定泄漏是可以的,则需要记录(大写),以备将来可能需要使用该程序的人员使用。

如果这是第三方库,您可能会被困住。但是一定要记录此泄漏发生。

但是基本上,如果内存泄漏是已知的数量(例如512 KB缓冲区)之类的,那么这不是问题。如果内存泄漏像每次调用库调用一样持续增长,则您的内存增加了512KB并且没有释放,那么您可能有问题。如果您记录下来并控制调用的执行次数,则它可能是可管理的。但是,您确实需要文档,因为虽然512的数量不多,但一百万个电话中的512个却很多。

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


3
“但是您绝对需要证明内存泄漏是一个有意识的决定。” 谢天谢地。到目前为止最好的一点。
食虫性的

15

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

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

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

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


5
喜欢雷蒙德·陈(Raymond Chen)的话:“建筑物已被拆除。不要费心扫地板,清空垃圾桶和擦除白板。也不要在建筑物出口排队,这样每个人都可以将他们移入/竭尽全力。您要做的就是让拆除团队等您完成这些毫无意义的房屋清洁任务。” (blogs.msdn.microsoft.com/oldnewthing/20120105-00/?p=8683
安德烈亚斯Magnusson的

11

我确定有人可以说出是的理由,但是不会是我。除了说不,我要说这不应该是/不是问题。有很多方法可以管理或遏制内存泄漏,而且许多系统都有此漏洞。

设备上有一些NASA系统正在为此计划。系统会每隔很长时间自动重新启动,以使内存泄漏不会对整体操作造成致命影响。只是一个遏制的例子。


这实际上是软件老化的一个例子。有趣的研究主题。
康拉德·鲁道夫,

经常自动重启,对吧?NASA,是吗?(**查看旧的Microsoft Windows安装CD **)这解释了很多……
Christian Severin

8

如果您分配内存并在程序的最后一行使用它,那不是泄漏。如果您分配内存而忘了它,即使内存量没有增长,那也是一个问题。分配但未使用的内存可能导致其他程序运行缓慢或根本无法运行。


并非如此,因为如果未使用它,它只会被分页。当应用程序退出时,将释放所有内存。

只要分配了它,其他程序就无法使用它。如果不取消分配,它将不会被分页。
比尔蜥蜴

当然会-这就是虚拟内存的全部意义。您可以有1 GB的实际RAM,但有4个进程,每个进程完全分配2 GB的虚拟内存(只要页面文件足够大)。

当然,如果这些进程中的每个进程都在积极使用所有内存,则会遇到令人讨厌的分页问题。

好的,我知道您现在在说什么。如果取消分配不使用的内存,则将减少分页的需求。如果你把它分配的,你的应用程序仍然会保持它,当它在分页回来了。
比尔的蜥蜴

8

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

因此,答案是非常合格的。

一个例子。如果您有一个单例资源,需要一个缓冲区来存储循环队列或双端队列,但又不知道该缓冲区需要多大,又负担不起锁定或每个读取器的开销,则可以分配一个指数倍的缓冲区,不释放旧内存将使每个队列/双端队列泄漏有限的内存。这些方法的好处是它们可以极大地加快每次访问的速度,并且可以从不冒争用锁的风险,从而可以改变多处理器解决方案的渐近性。

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

从技术上讲,这是两种情况,但在大小上都是有限制的,在可扩展的圆形工作窃取双端队列情况下,以巨大的性能优势换取了队列内存使用量增加2的限制。


1
您可以使用危险指示器来防止泄漏。
Demi 2013年

8

如果在程序开始时分配了一堆堆,而在退出时没有释放堆,那么这本身就不是内存泄漏。内存泄漏是指您的程序遍历一段代码,然后该代码分配堆,然后“失去跟踪”而不释放它。

实际上,您无需在退出前就调用free()或删除它。当进程退出时,其操作系统将回收其所有内存(POSIX肯定是这种情况。在其他OS(尤其是嵌入式OS)上,YMMV)。

我在退出时不释放内存的唯一警告是,如果您曾经重构过程序,以使其成为例如等待输入的服务,则您的程序会做任何事情,然后循环等待另一个服务调用,那么您编码的内容可能会变成内存泄漏。


我不敢苟同。那就是 “内存泄漏本身”。
康拉德·鲁道夫

在您“丢失”对该对象的引用之前,这不是泄漏。据推测,如果在程序的整个生命周期中都使用了内存,那么它就不会泄漏。如果在调用exit()之前引用没有丢失,则绝对不是泄漏。
nsayer

Amiga DOS是最后一个未在流程后面清除的O / SI。但是请注意,即使没有进程在使用System V IPC共享内存,也可以保留它。
乔纳森·勒夫勒

在您进行热同步之前,Palm不会释放“泄漏”的内存。到了amiga之后,一切都很好。我在有泄漏的手掌模拟器上运行了应用程序。它们从未真正进入我的手掌。
baash05,2009年

6

这是特定于域的,因此几乎不值得回答。用你疯狂的头

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

并且存在一系列中间情况。

延迟产品发布以修复所有问题的机会成本($$$),但最糟糕的内存泄漏通常会使任何“草率或不专业”的感觉相形见war。你的老板付钱给你是为了赚钱,而不是为了获得温暖,模糊的感觉。


2
态度很短视。您基本上是说,在发现由那些习惯造成的缺陷之前,无需使用从根本上讲的编程习惯。问题在于,使用草率方法编写的软件往往比没有采用草率方法编写的软件具有更多的缺陷。
John Dibling

1
我不相信所有。而且内存管理比编写干净的方法还要复杂。
达斯汀·盖茨

1
达斯汀显然像我们大多数人一样在现实世界中工作,我们一直在疯狂的期限内努力跟上竞争。因此,应以务实的方式处理错误。通过在无关紧要的程序上浪费大量时间来解决不重要的错误,您将无法完成工作。
Wouter van Nifterick 09年

这种态度的问题是:什么时候开始修复泄漏?“好吧,这是一座发电厂,但它只是煤炭,而不是铀。为什么在这里修复泄漏?” -我在现实世界中了解到,如果您从一开始就没有做正确的事情,那么它永远不会发生。这种态度会使两周后“ 99%完成”的项目得到滋生,并保持两个月。
peterchen

5

您必须首先意识到,感知的内存泄漏与实际的内存泄漏之间存在很大的差异。分析工具经常会报告许多红色鲱鱼,并将某些东西标记为实际上没有泄漏的东西(内存或诸如句柄之类的资源)。通常,这是由于分析工具的体系结构所致。例如,某些分析工具会将运行时对象报告为内存泄漏,因为它从未看到那些对象被释放。但是重新分配发生在运行时的关闭代码中,分析工具可能看不到该代码。

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

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


5

在这种问题中,上下文就是一切。我个人无法忍受泄漏,在我的代码中,我竭尽全力修复泄漏,但是修复泄漏并不总是值得的,而且当人们偶尔按小时支付我工资时告诉他们,修复他们的代码漏洞对我来说不值得。让我举一个例子:

我当时正在对一个项目进行分类,进行一些性能工作并修复了许多错误。我追踪并完全理解了应用程序初始化期间发生的泄漏。正确地修复它可能需要一天左右的时间才能重构出其他可以正常运行的代码。我本可以做些骇人听闻的事情(例如将值填充到一个全局变量中,并在某个我知道它不再可以免费使用的点上抓住它),但这会使下一个不得不接触代码的人更加困惑。

就我个人而言,我不会一开始就那样编写代码,但是我们大多数人不会总是在原始的,精心设计的代码库上工作,有时您必须务实地看待这些东西。我花了修复150字节泄漏的时间,而不是花在进行算法改进上,从而减少了兆字节的内存。

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


聪明。尤其是由于泄漏发生在初始化期间,这意味着泄漏不会在应用程序的运行时累积。
黛咪

5

虽然大多数答案都集中在实际内存泄漏上(这永远都是不可能的,因为它们是草率的编码),但这一部分问题对我来说似乎更有趣:

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

如果使用了关联的内存,则无法在程序结束前释放它。释放是由程序退出还是由操作系统完成并不重要。只要对此进行了记录,以便更改不会引起实际的内存泄漏,并且只要图片中不涉及C ++析构函数或C清除函数即可。未关闭的文件可能是通过泄漏的FILE对象显示的,但是缺少的fclose()可能还会导致不刷新缓冲区。

因此,回到原始情况,恕我直言,它本身是完全可以的,以至于最强大的检漏仪之一Valgrind仅在需要时才会处理此类泄漏。在Valgrind上,如果您不事先释放指针而覆盖了指针,则会将其视为内存泄漏,因为它更有可能再次发生并导致堆不断增长。

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


哇...知道什么是内存泄漏的人。
西蒙·布坎

4

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

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

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

因此,如果您有泄漏,请通过两种方法评估其影响

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

Foredecker


3.对软件维护的影响。
peterchen

3

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

对我而言,这就像在问“没有人在附近时,我可以在凌晨3点打破红灯吗?”。可以肯定的是,它当时可能不会造成任何麻烦,但是它将为您在高峰时段做同样的事情提供帮助!


3

不,您不应泄漏操作系统将为您清除的泄漏。原因(就我所能检查的问题而言,在上面的答案中没有提及)是因为您永远不知道main()何时会在另一个程序中用作函数/模块。如果您的main()在另一个人的软件中成为经常调用的功能-该软件将发生内存泄漏,并随着时间的推移而占用内存。

基辅


3

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


3

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

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

占用越来越多内存的进程不一定会泄漏。只要它能够引用和释放该内存,它就将一直处于该进程的显式控制之下,并且不会泄漏。该过程可能设计得很糟糕,尤其是在内存有限的系统环境中,但这与泄漏是不一样的。相反,即使泄漏的内存量很小,丢失32字节缓冲区的范围仍然是泄漏。如果您认为这无关紧要,请等到有人在您的库调用周围包装算法并将其调用10,000次。

我看不出有任何理由允许您的代码泄漏,尽管很小。诸如C和C ++之类的现代编程语言竭尽所能来帮助程序员防止此类泄漏,并且很少有人会提出不采用良好的编程技术(尤其是在与特定语言工具结合使用时)以防止泄漏的论点。

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

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


2

如果它是有意的,那么它实际上不是泄漏,除非有大量内存,否则可能会成为问题,否则可能会增长为大量内存。在程序生命周期中不清除全局分配是很常见的。如果泄漏发生在服务器或长时间运行的应用程序中,并且随着时间的流逝而增加,那么这就是一个问题。


2

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

我使用的旧版服务器应用程序本来应该是坚如磐石,但它们存在漏洞,并且全局变量确实会妨碍内存检测工具。这很重要。

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


2

我看到了与所有此类场景问题相同的问题:程序更改时会发生什么,突然间,几乎没有内存泄漏被调用一千万次,而程序的末尾位于另一个位置,这有关系吗?如果它在库中,请向库维护者记录错误,请不要在自己的代码中泄漏任何内容。


在这种情况下,内存泄漏的影响会改变,您需要重新评估堵塞漏洞的优先级。
John Dibling

@John:最好至少记录下泄漏。即使那样,我也不相信有人不会忽略大的红色闪烁注释和复制并粘贴泄漏的代码。我不想一开始就不给某人这样做的能力。
偷渡

2

我会回答不。

从理论上讲,如果您留下一团糟,操作系统将在您清理后清理(现在这很不礼貌,但是由于计算机没有感觉,所以可以接受)。但是您无法预期程序运行时可能发生的所有可能情况。因此(除非您能够对某些行为进行形式上的证明),从专业的角度来看,造成内存泄漏只是不负责任的行为。

如果第三方组件泄漏了内存,那么这是一个强烈反对使用内存的理由,这不仅是因为迫在眉睫的效果,而且还因为它表明程序员工作松懈,而且这可能还会影响其他指标。现在,在考虑旧系统时,这很困难(考虑一下Web浏览组件:据我所知,它们都会泄漏内存),但这应该成为常态。


2

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

这是一个示例,在Sun 3时代的SunOS上,如果某个进程使用exec(或更传统的是fork然后是exec),则会出现一个问题,随后的新进程将继承与父进程相同的内存占用,并且无法缩减。如果父进程分配了1/2 gig的内存并且在调用exec之前没有释放它,则子进程将开始使用相同的1/2 gig(即使未分配)。此行为最好由SunTools(它们的默认窗口系统)表现出来,这是一个内存消耗。它产生的每个应用程序都是通过fork / exec创建的,并继承了SunTools的外观,从而迅速填充了交换空间。


2

这已经在ad nauseam中讨论过了。底线是内存泄漏是一个错误,必须修复。如果第三方库泄漏了内存,那会让人怀疑它还有什么问题,不是吗?如果要建造汽车,您会使用偶尔漏油的发动机吗?毕竟,有人制造了引擎,所以这不是您的错,您也无法修复,对吧?


但是,如果您拥有的发动机偶尔会漏油,您会花钱修理它,还是留意油位并不时加油。答案取决于各种因素。
苗条的

这与拥有汽车无关。这是关于制造汽车。如果您获得了带有内存泄漏的第三方库,而您绝对必须使用它,那么您就可以使用它。但是,如果您是编写系统或库的人,则有责任确保它没有错误。
Dima

+1像对待其他任何bug一样对待它。(这并不意味着我的书中的“立即修复”,而是“肯定需要修复”)
peterchen

2

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

您对设计为不会退出的Server程序怎么办?

如果您是那种不会在分配和正确分配资源的情况下不设计和实现代码的程序员,那么我不想与您或您的代码有任何关系。如果您不希望清理泄漏的内存,那么锁又如何呢?你也把它们挂在外面吗?您是否会在各种目录中留下一些临时文件?

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

自己清理一下。哟,妈妈不再在这里工作了。


我研究的服务器程序故意使用进程而不是线程,因此内存泄漏和分段错误会造成有限的损害。
苗条的

有趣的方法。我会有点担心进程无法退出并继续吞噬内存。
EvilTeach

2

通常,如果您感到无法避免的内存泄漏,则需要对对象所有权进行更认真的考虑。

但是,对于您的问题,我的概括地说是在生产代码中,是的。在开发过程中,否。这似乎是倒退,但这是我的理由:

在您描述的情况下,内存一直保存到程序结束,不释放内存是完全可以的。退出过程后,操作系统仍将清理。实际上,这可能会改善用户的体验:在我开发的游戏中,程序员认为退出前释放所有内存会更干净,从而导致程序关闭最多需要半分钟!一项名为exit()的快速更改使该过程立即消失,并将用户带回他想要的桌面。

但是,您对调试工具的看法是正确的:它们会很合适,并且所有误报都可能使查找您的真实内存泄漏变得很痛苦。因此,请务必编写调试代码以释放内存,并在发货时将其禁用。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.