当您在程序终止之前没有释放 malloc 时,会发生什么?

What REALLY happens when you don't free after malloc before program termination?

提问人:Scott 提问时间:3/17/2009 最后编辑:user438383Scott 更新时间:8/16/2023 访问量:170289

问:

我们都被教导说,你必须释放分配的每个指针。不过,我有点好奇不释放内存的真正代价。在一些明显的情况下,例如在循环或线程执行的一部分中调用时,释放非常重要,这样就不会发生内存泄漏。但请考虑以下两个示例:malloc()

首先,如果我有代码,那是这样的:

int main()
{
    char *a = malloc(1024);
    /* Do some arbitrary stuff with 'a' (no alloc functions) */
    return 0;
}

这里真正的结果是什么?我的想法是,进程死了,然后堆空间无论如何都消失了,所以错过调用并没有什么坏处(但是,我确实认识到无论如何拥有它对于闭合、可维护性和良好实践的重要性)。我的想法是对的吗?free

其次,假设我有一个程序,它有点像 shell。用户可以声明变量,例如,这些变量存储在一些动态数据结构中以备后用。显然,很明显,你会使用一些解决方案来调用一些 *alloc 函数(哈希图、链表等)。对于这种程序,在调用后释放是没有意义的,因为在程序执行期间,这些变量必须始终存在,并且没有好的方法(我可以看到)使用静态分配的空间来实现这一点。有一堆内存被分配,但只是作为进程结束的一部分释放,这是糟糕的设计吗?如果是这样,还有什么替代方案?aaa = 123malloc

c malloc 免费

评论

34赞 SO Stinks 3/4/2015
下面的人一直说一个好的现代操作系统确实会清理,但如果代码在内核模式下运行(例如,出于性能原因)怎么办?内核模式程序(例如在 Linux 中)是否经过沙盒处理?如果没有,我相信你需要手动释放所有内容,我想,甚至在任何异常终止之前,比如 abort()。
11赞 zwol 12/7/2016
@Dr.PersonPersonII 是的,在内核模式下运行的代码通常必须手动释放所有内容。
6赞 Marco Bonelli 8/16/2019
我想补充一点,它并没有真正做任何事情来实际释放内存!它只是重置 malloc 的 libc 实现中的一些指针,这些指针跟踪大型 mmmapping 内存页(通常称为“堆”)中的可用内存块。该页面仍将仅在程序终止时释放,而不是在程序终止之前。free(a)
2赞 glglgl 8/29/2019
@MarcoBonelli 部分正确。如果 ed 内存来自“正常”的 sbrk 堆,并且位于其末尾,则调用以减少内存映像。如果通过 分配内存,则在 中取消映射。malloc()sbrk()malloc()mmap()free()
2赞 forest 5/22/2021
@SOStinks内核“程序”不会像用户空间程序那样退出。内核代码“现有”的等价物是系统关闭,在这种情况下,您不需要释放它。只是在内核中,您通常运行的时间很长,以至于很少有不需要释放内存的情况。

答:

130赞 compie 3/17/2009 #1

是的,你是对的,你的例子不会造成任何伤害(至少在大多数现代操作系统上不会)。进程退出后,操作系统将恢复进程分配的所有内存。

来源:Allocation 和 GC Myths(PostScript 警报!

分配误区 4:非垃圾回收程序 应始终释放所有内存 他们分配。

真相:省略 经常执行的解除分配 代码导致泄漏越来越多。他们是 很少可以接受。但是程序 保留大部分已分配的内存,直到 程序退出通常性能更好 没有任何干预的解除分配。 如果出现以下情况,Malloc 更容易实现 没有免费的。

在大多数情况下,解除分配内存 在程序退出之前是没有意义的。无论如何,操作系统都会回收它。自由 将触摸和翻页在死里 对象;操作系统不会。

后果:小心“泄漏 检测器“,用于计算分配。 一些“泄漏”是好的!

也就是说,您真的应该尽量避免所有内存泄漏!

第二个问题:你的设计没问题。如果您需要在应用程序退出之前存储某些内容,那么可以使用动态内存分配来执行此操作。如果预先不知道所需的大小,则无法使用静态分配的内存。

评论

3赞 DevinB 3/17/2009
可能是因为,正如我所读到的那样,问题是泄露的内存实际上发生了什么,而不是这个特定的例子是否正常。不过,我不会否决它,因为它仍然是一个很好的答案。
3赞 Pete Kirkham 3/17/2009
可能曾经有过(早期的 Windows、早期的 Mac OS),也许现在仍然是,操作系统需要进程在退出之前释放内存,否则空间不会被回收。
14赞 Sebastian Mach 3/24/2009
我认为通过说“因为检漏仪”来解释 free() 内存的必要性是错误的。这就像说“你必须在游乐街上慢慢开,因为警察可能会拿着测速摄像头等你”。
6赞 Jordan Brown 11/26/2019
事实上,即使在长时间运行的程序中,一次性的适度大小的泄漏也不是问题。(强调“一次性”部分。然而,最好的做法仍然是清理它,这样验证器就不会发牢骚——不是因为关闭验证器本身就很有用,而是因为如果你的验证输出中有一堆“可接受的”失败,那么找到不可接受的失败就更难了。
1赞 Christian Hujer 4/25/2022
引用的论文是用UNIX系统编写的。论文中关于操作系统回收内存的声明是不可移植的!定义函数的 ISO/IEC 9899 不要求程序终止(如 、、)隐式释放内存。告诉人们不打电话是可以的,这在 Linux、Windows 和大多数(所有?Unix 系统。但这仍然是一个糟糕的建议:在嵌入式/物联网和复古领域,还有无数其他托管环境,不调用会产生不良后果。malloc()exit()abort()_Exit()free()free()
450赞 Paul Tomblin 3/17/2009 #2

几乎每个现代操作系统都会在程序退出后恢复所有分配的内存空间。我能想到的唯一例外可能是像 Palm OS 这样的东西,其中程序的静态存储和运行时内存几乎是一回事,因此不释放可能会导致程序占用更多存储空间。(我只是在这里推测。

因此,一般来说,它没有什么坏处,除了存储空间超出所需空间的运行时成本。当然,在你给出的示例中,你希望保留一个变量的内存,直到它被清除为止。

但是,一旦您不再需要内存,并释放您在程序退出时仍然存在的任何内容,这被认为是一种很好的风格。这更像是一种练习,了解您正在使用什么内存,并考虑是否仍然需要它。如果不跟踪,则可能会发生内存泄漏。

另一方面,在退出时关闭文件的类似告诫有一个更具体的结果 - 如果你不这样做,你写给它们的数据可能不会被刷新,或者如果它们是临时文件,它们可能不会在你完成后被删除。此外,数据库句柄应提交其事务,然后在完成处理后关闭。同样,如果您使用的是面向对象的语言,如 C++ 或 Objective C,则在完成对象后不释放对象将意味着析构函数永远不会被调用,并且类负责的任何资源可能不会被清理。

评论

24赞 user105033 11/13/2009
值得一提的是,并非每个人都在使用现代操作系统,如果有人使用您的程序(并且它仍然在不恢复内存的操作系统上运行)运行它,那么 GG。
154赞 Paul Tomblin 1/1/2010
“但是,一旦你不再需要内存,并释放你在程序退出时仍然拥有的任何内容,这被认为是很好的风格。 那么,你认为错了吗?
34赞 Paul Tomblin 1/2/2010
如果你有一个内存存储,直到程序退出的那一刻,你才需要它,并且你没有在原始操作系统上运行,那么在你退出之前释放内存是一种风格上的选择,而不是一个缺陷。
41赞 Heath Hunnicutt 6/25/2011
@Paul -- 仅仅同意 EvilTeach,不被认为是释放内存的好风格,不释放内存是不正确的。你的措辞使这似乎与戴与领带相配的手帕一样重要。实际上,这是在穿裤子的层面上。
13赞 R.. GitHub STOP HELPING ICE 1/29/2012
最后一段是完全错误的。在 C 语言中,正常程序终止 via 或 implicit when return from 关闭并刷新所有打开的文件。exitexitmain
5赞 Kyle Cronin 3/17/2009 #3

不释放变量并没有真正的危险,但是如果将指向内存块的指针分配给另一个内存块而不释放第一个块,则第一个块将不再可访问,但仍会占用空间。这就是所谓的内存泄漏,如果你有规律地这样做,那么你的进程将开始消耗越来越多的内存,从其他进程中夺走系统资源。

如果该过程是短暂的,您通常可以这样做,因为当该过程完成时,所有分配的内存都会作系统回收,但我建议养成释放所有内存的习惯,您没有进一步使用。

评论

2赞 Dan Bechard 4/12/2016
@KyleCronin我宁愿有段错误而不是内存泄漏,因为两者都是严重的错误,段错误更容易检测。内存泄漏经常被忽视或未解决,因为它们是“非常良性的”。我和我的RAM完全不同意。
1赞 Kyle Cronin 4/12/2016
@Dan 作为开发人员,当然可以。作为用户,我将承担内存泄漏。我宁愿拥有可以工作的软件,尽管内存泄漏,而不是不能工作的软件。
0赞 Dan Bechard 4/12/2016
@KyleCronin再说一次,我不会。作为用户,我使用过具有“小”内存泄漏的软件,这些软件会随着时间的推移不断降低性能。这是我经历过的最令人沮丧的经历,那里有无法解释的蓝屏死机。我宁愿软件崩溃并直接告诉我它不起作用,这样我就可以继续我的生活,而不是抱着错误的希望,如果我再浪费 6 个小时,我可能会完成我的工作。就像DevinB说的,我认为我们都宁愿消除两者,而不是争论哪个更烦人。;)
0赞 Dan Bechard 4/12/2016
总而言之,“我宁愿拥有可以工作的软件,尽管内存会泄漏,而不是不能工作的软件”,这是我们从根本上不同意的地方。内存泄漏的软件无法“工作”。
16赞 Antti Huima 3/17/2009 #4

退出时保持内存未释放是完全可以的;malloc() 从称为“堆”的内存区域分配内存,当进程退出时,进程的完整堆被释放。

话虽如此,人们仍然坚持认为在退出之前释放所有内容是件好事的一个原因是内存调试器(例如 Linux 上的 valgrind)将未释放的块检测为内存泄漏,如果您也有“真正的”内存泄漏,如果您最后也得到“假”结果,则更难发现它们。

评论

1赞 Christoffer 3/17/2009
Valgrind 在区分“泄露”和“仍然可以访问”方面做得不是很好吗?
13赞 DevinB 3/17/2009
-1 表示“完全正常” 在不释放内存的情况下保留已分配的内存是不好的编码做法。如果该代码被提取到库中,那么它会导致到处都是内存泄漏。
7赞 R.. GitHub STOP HELPING ICE 1/29/2012
+1 补偿。请看compie的回答。 在当时被认为是有害的。freeexit
11赞 Bill the Lizard 3/17/2009 #5

如果你使用的是你分配的内存,那么你没有做错任何事情。当您编写函数(main 除外)时,它会成为一个问题,这些函数分配内存而不释放内存,也没有将其提供给程序的其余部分。然后,您的程序继续运行,并分配了该内存,但无法使用它。您的程序和其他正在运行的程序被剥夺了该内存。

编辑:说其他正在运行的程序被剥夺了该内存并不是100%准确的。操作系统总是可以让他们使用它,但代价是将程序换成虚拟内存()。但是,关键是,如果您的程序释放了它未使用的内存,那么虚拟内存交换就不太可能是必要的。</handwaving>

3赞 Michael 3/17/2009 #6

你是对的,当进程退出时,内存会自动释放。有些人在进程终止时尽量不进行大规模清理,因为它将全部交给操作系统。但是,在程序运行时,应释放未使用的内存。如果不这样做,则在工作集过大时,最终可能会用完或导致过多的分页。

2赞 ojrac 3/17/2009 #7

如果您从头开始开发应用程序,则可以就何时免费拨打电话做出一些明智的选择。你的示例程序很好:它分配内存,也许你让它工作几秒钟,然后关闭,释放它声称的所有资源。

但是,如果你正在编写其他任何东西——一个服务器/长时间运行的应用程序,或者一个供其他人使用的库,你应该期望在你所恶意的所有东西上免费调用。

暂且忽略务实的一面,遵循更严格的方法,强迫自己释放你所恶意的一切,要安全得多。如果您不习惯在编码时注意内存泄漏,那么很容易就会出现一些泄漏。所以换句话说,是的——你可以在没有它的情况下逃脱;不过,请小心。

3赞 Uri 3/17/2009 #8

在这方面,你是绝对正确的。在小型琐碎程序中,变量必须存在直到程序死亡,解除分配内存没有真正的好处。

事实上,我曾经参与过一个项目,每次执行程序都非常复杂,但相对来说是短暂的,我的决定是保持内存分配,而不是通过错误释放来破坏项目的稳定性。

话虽如此,在大多数程序中,这并不是一个真正的选项,否则它可能会导致内存不足。

-2赞 mouviciel 3/17/2009 #9

我认为你的两个例子实际上只有一个:应该只在过程结束时发生,正如你所指出的,这是无用的,因为过程正在终止。free()

但是,在第二个示例中,唯一的区别是允许未定义数量的 ,这可能会导致内存不足。处理这种情况的唯一方法是检查 的返回代码并采取相应的行动。malloc()malloc()

73赞 Trevor Boyd Smith 3/18/2009 #10

=== 未来证明代码重用呢?===

如果您编写代码来释放对象,那么您将代码限制为仅在您可以依赖关闭进程释放的内存时才能安全使用......即小型一次性使用项目或“一次性”[1] 项目)......您知道该过程何时结束。

如果你确实编写了 free() 所有动态分配的内存的代码,那么你就是在为代码的未来辩护,并让其他人在更大的项目中使用它。


[1] 关于“抛弃”项目。“扔掉”项目中使用的代码有一种不被扔掉的方法。接下来你知道十年过去了,你的“丢弃”代码仍在使用)。

我听说过一个故事,说有人写了一些代码,只是为了好玩,让他的硬件更好地工作。他说“只是一种爱好,不会是大而专业的”。多年后,很多人都在使用他的“爱好”代码。

评论

22赞 Dan Bechard 7/15/2017
对“小项目”投了反对票。有许多大型项目非常有意地在退出时释放内存,因为如果您知道目标平台,这是浪费时间。IMO,一个更准确的例子是“孤立的项目”。例如,如果你正在制作一个可重用的库,它将包含在其他应用程序中,那么没有明确定义的出口点,所以你不应该泄漏内存。对于独立应用程序,您将始终确切地知道进程何时结束,并且可以有意识地决定将清理工作卸载到操作系统(无论哪种方式都必须进行检查)。
8赞 Adrian McCarthy 3/15/2018
昨天的应用程序是今天的库函数,明天它将链接到一个长期调用它数千次的服务器。
2赞 supercat 4/25/2018
@AdrianMcCarthy:如果一个函数检查静态指针是否为 null,如果为 null,则对其进行初始化,如果指针仍为 null,则终止,即使从未调用过,也可以安全地使用任意次数的函数。我认为可能值得区分内存泄漏,前者会消耗无限量的存储空间,而后者只会浪费有限且可预测的存储量。malloc()free
0赞 Adrian McCarthy 4/25/2018
@supercat:我的评论是关于代码随时间的变化。当然,泄漏有限数量的内存不是问题。但总有一天,有人会想要改变这个函数,这样它就不再使用静态指针了。如果代码没有提供能够释放指向内存的规定,那将是一个艰难的更改(或者,更糟糕的是,更改将是糟糕的,你最终会得到无限的泄漏)。
2赞 supercat 4/25/2018
@AdrianMcCarthy:将代码更改为不再使用静态指针可能需要将指针移动到某种“上下文”对象中,并添加代码来创建和销毁此类对象。如果指针始终为“不存在分配”,而“指针”在“分配确实存在”时为非 null,则让代码释放分配并将指针设置为上下文销毁时将很简单,尤其是与将静态对象移动到上下文结构中所需的其他所有操作相比。nullnull
14赞 sharptooth 3/18/2009 #11

这段代码通常可以正常工作,但要考虑代码重用的问题。

您可能编写了一些代码片段,这些代码片段不会释放分配的内存,它的运行方式是自动回收内存。似乎没问题。

然后,其他人将您的代码片段复制到他的项目中,使其每秒执行一千次。那个人现在在他的程序中有一个巨大的内存泄漏。一般不是很好,通常对服务器应用程序来说是致命的。

代码重用在企业中很常见。通常,公司拥有其员工产生的所有代码,每个部门都可以重用公司拥有的任何代码。因此,通过编写这种“看起来无辜”的代码,你可能会给其他人带来麻烦。这可能会让你被解雇。

评论

4赞 supercat 11/16/2014
值得一提的是,不仅有人复制了代码片段,而且还可能编写了某个程序,该程序一旦被修改为重复执行某些特定操作。在这种情况下,可以分配一次内存,然后重复使用而不释放内存,但为每个操作分配和放弃内存(不释放内存)可能是灾难性的。
26赞 user50049 3/18/2009 #12

一旦我确定我完成了它,我通常会释放每个分配的块。今天,我的程序的入口点可能是 ,但明天它可能是,并键入为函数指针。main(int argc, char *argv[])foo_entry_point(char **args, struct foo *f)

所以,如果发生这种情况,我现在有一个泄漏。

关于你的第二个问题,如果我的程序接受像 a=5 这样的输入,我会为 a 分配空间,或者在随后的 a=“foo” 上重新分配相同的空间。这将保持分配状态,直到:

  1. 用户键入了“unset a”
  2. 输入了我的清理功能,要么是处理信号,要么是用户键入“退出”

我想不出任何现代操作系统在进程退出后不会回收内存。话又说回来,free()很便宜,为什么不清理呢?正如其他人所说,像 valgrind 这样的工具非常适合发现您确实需要担心的泄漏。即使您示例的块将被标记为“仍然可以访问”,当您试图确保没有泄漏时,它只是输出中的额外噪音。

另一个误区是“如果它在 main() 中,我不必释放它”,这是不正确的。请考虑以下几点:

char *t;

for (i=0; i < 255; i++) {
    t = strdup(foo->name);
    let_strtok_eat_away_at(t);
}

如果这是在分叉/守护进程之前发生的(理论上永远运行),那么你的程序刚刚泄漏了 t 255 次的未确定大小。

一个好的、写得很好的程序应该总是在自己之后清理。释放所有内存、刷新所有文件、关闭所有描述符、取消链接所有临时文件等。此清理功能应在正常终止或收到各种致命信号时达到,除非您想留下一些文件,以便检测崩溃并恢复。

真的,善待那些可怜的灵魂,当你继续做其他事情时,他们必须维护你的东西......把它交给他们“Valgrind Clean”:)

评论

1赞 Lie Ryan 3/20/2013
free() is cheap除非你有十亿个具有复杂关系的数据结构,你必须一个接一个地释放,否则遍历数据结构以尝试释放所有内容最终可能会大大增加你的关闭时间,特别是如果该数据结构的一半已经分页到磁盘上,没有任何好处。
6赞 user50049 8/26/2014
@LieRyan如果你有十亿个,就像字面上的十亿个结构一样,你肯定还有其他需要专门考虑的问题——远远超出了这个特定答案的范围:)
61赞 DigitalRoss 12/28/2009 #13

你是对的,没有造成伤害,退出会更快

造成这种情况的原因有很多:

  • 所有桌面和服务器环境都只是在 exit() 上释放整个内存空间。他们不知道程序内部数据结构,例如堆。

  • 无论如何,几乎所有的实现都不会将内存返回给操作系统。free()

  • 更重要的是,在 exit() 之前完成是浪费时间。在退出时,内存页和交换空间被简单地释放。相比之下,一系列 free() 调用将消耗 CPU 时间,并可能导致磁盘分页操作、缓存未命中和缓存逐出。

关于未来代码重用的可能性,证明无意义运维的确定性:这是一个考虑因素,但可以说它不是敏捷的方式。雅格尼!

评论

3赞 user106740 12/28/2009
我曾经做过一个项目,我们花了很短的时间试图了解程序的内存使用情况(我们被要求支持它,我们没有编写它)。根据经验,我轶事同意你的第二颗子弹。但是,我想听到您(或某人)提供更多证据来证明这是真的。
4赞 user106740 12/29/2009
没关系,找到了答案:stackoverflow.com/questions/1421491/...。谢谢!
8赞 Adrian McCarthy 4/25/2018
YAGNI 原则是双向的:您永远不需要优化关闭路径。过早的优化等等。
0赞 Peter - Reinstate Monica 5/12/2021
您可以提到,在退出之前释放内存不仅浪费了运行时间,还浪费了开发人员和潜在的测试时间。
0赞 Christian Hujer 4/25/2022
“敏捷”的论点是错误的。这也是敏捷的:给定两个或多个其他大致相同的选项,选择使未来更改更容易的路径。这也可能是之前调用的路径:当程序发展时,它可能不一定保留在程序的末尾。因此,“敏捷”的论点是“视情况而定”。free()exit()exit()
46赞 dhein 5/8/2015 #14

我完全不同意所有说OP是正确的或没有伤害的人。

每个人都在谈论现代和/或遗留的操作系统。

但是,如果我所处的环境根本没有操作系统怎么办? 哪里什么都没有?

想象一下,现在您正在使用线程样式的中断并分配内存。 在 C 标准中,ISO/IEC:9899 是内存的寿命,表示为:

7.20.3 内存管理功能

1 连续调用 calloc 所分配的存储的顺序和连续性, malloc 和 realloc 函数未指定。指针返回时分配 succeeds 适当对齐,以便可以将其分配给指向任何类型对象的指针 然后用于访问分配的空间中的此类对象或此类对象的数组 (直到显式解除分配空间)。已分配对象的生存期延长 从分配到解除分配。[...]

因此,不必让环境为你做解放的工作。 否则,它将被添加到最后一句话中:“或者直到程序终止。

换句话说: 不释放内存不仅仅是不好的做法。它生成不可移植且不符合 C 的代码。 这至少可以看作是“正确的,如果以下情况:[...],受环境支持”。

但是在你根本没有操作系统的情况下,没有人为你做这项工作 (我知道您通常不会在嵌入式系统上分配和重新分配内存, 但在某些情况下,您可能希望这样做。

因此,一般来说,普通 C(OP 被标记为), 这只是产生错误且不可移植的代码。

评论

12赞 John Go-Soco 4/21/2016
一个反驳的论点是,如果你是一个嵌入式环境,那么你作为开发人员,首先在内存管理方面会更加挑剔。通常,这实际上就是预先预先分配静态固定内存,而不是根本没有任何运行时 mallocs/reallocs。
5赞 dhein 4/21/2016
@lunarplasma:虽然你说的不是错的,但这并不能改变语言标准所陈述的事实,每个反对/推进它的人,甚至可能是常识,都在生成有限的代码。如果有人说“我不必关心它”,我可以理解,因为有足够多的情况是可以的。但是,至少应该知道为什么他不必在乎。特别是不要省略它,因为一个问题与该特殊情况无关。而且由于 OP 在理论(学校)方面一般询问 C。说“你不需要”是不行的!
7赞 supercat 10/15/2016
在大多数没有操作系统的环境中,程序无法“终止”。
2赞 dhein 10/31/2016
@supercat:正如我之前所写的:你是对的。但是,如果有人在教学原因和学校方面询问它,那么说“你不需要考虑它,大多数时候都没关系”是不对的 语言定义的措辞和行为是有原因的,只是因为大多数环境都为你处理它, 你不能说没有必要关心。这就是我的观点。
6赞 2/16/2017
-1 引用 C 标准,而其中大部分不适用于没有操作系统的情况,因为没有运行时来提供标准要求的功能,尤其是关于内存管理和标准库函数(这些功能也明显与运行时/操作系统一起缺失)。
13赞 DevSolar 1/13/2016 #15

这里真正的结果是什么?

您的程序泄漏了内存。根据您的操作系统,它可能已恢复。

大多数现代桌面操作系统确实会在进程终止时恢复泄漏的内存,因此忽略该问题很可悲(从此处的许多其他答案可以看出这一点)。

但是你所依赖的安全功能不是语言的一部分,你不应该依赖它。您的代码可能会在下次此行为导致“硬”内存泄漏的系统上运行。

您的代码最终可能会在内核模式下运行,或者在不使用内存保护作为权衡的老式/嵌入式操作系统上运行。(MMU 占用芯片空间,内存保护会花费额外的 CPU 周期,要求程序员自己清理并不过分)。

您可以随意使用和重用内存(和其他资源),但请确保在退出之前取消分配所有资源。


我想我应该添加这个小小的历史瑰宝,来自 Amiga 的 Rom 内核手册的屏幕截图,即官方平台文档。

"In fact, Amiga programmers need to be careful with every system resource, not just memory. All system resources from audio channels to the floppy disk drives are shared among tasks. Before using a resource, you must ask the system for access to the resource. This may fail if the resource is already being used by another task. --- Once you have control of a resource, no other task can use it, so give it up as soon as you are finished. When your program exits, you must give everything back whether it's memory, access to a file, or an I/O port. You are responsible for this, the system will not do it for you automatically."

评论

0赞 supercat 10/26/2020
在应用程序可能在操作系统没有意识到的情况下使用 DMA 之类的东西的平台上 [曾经在 PC 上使用操作系统未考虑的硬件时很常见,或者在 Amiga 上使用硬件实现比操作系统容纳的更好的图形和声音时很常见],让操作系统在应用程序退出时保留已分配的内存将导致存储泄漏, 这可能会导致内存耗尽,但释放即将由 DMA 操作写入的存储是内存损坏的秘诀。
0赞 DevSolar 10/26/2020
@supercat 这只是另一个资源泄漏,只是另一种类型。当然,在释放内存之前,您必须向可能访问内存的任何人注销内存。您也无法将当前是 IPC 消息的内存释放到另一个进程。
0赞 supercat 10/26/2020
我的观点是,在某些平台上,可能存在操作系统一无所知的内存用户。理想情况下,操作系统应该有单独的“分配将在退出时释放的内存”和“分配不得隐式释放的内存,因为操作系统可能一无所知”调用,但我不知道 Amiga 是否做到了,而 PC 肯定没有。
2赞 Richie 10/17/2021
在 Amiga exec.library 中,使用 AllocMem() 后不调用 Free() 将使内存“丢失”,直到重新启动,malloc 和 free 将在后台使用它们。
1赞 DevSolar 10/18/2021
@Richie 这正是我写答案时的想法。某些操作系统会在您之后进行清理,但这并不能改变您首先泄漏资源的事实。
7赞 spenceryue 10/17/2017 #16

实际上,OSTEP在线教科书中有一个操作系统本科课程的部分,其中确切地讨论了您的问题。

相关部分是第 6 页内存 API 一章中的“忘记释放内存”,其中给出了以下解释:

在某些情况下,不调用 free() 似乎是合理的。为 例如,您的程序是短暂的,很快就会退出;在这种情况下, 当进程终止时,操作系统将清理其所有分配的页面,并 因此,本身不会发生内存泄漏。虽然这当然“有效” (见第 7 页的旁白),这可能是一个坏习惯,所以要小心 选择这样的策略

本摘录是在介绍虚拟内存概念的背景下进行的。基本上,在本书的这一点上,作者解释说,操作系统的目标之一是“虚拟化内存”,也就是说,让每个程序都相信它可以访问非常大的内存地址空间。

在后台,操作系统会将用户看到的“虚拟地址”转换为指向物理内存的实际地址。

但是,共享资源(如物理内存)需要操作系统跟踪哪些进程正在使用它。因此,如果进程终止,则在操作系统的功能和设计目标范围内,可以回收进程的内存,以便它可以重新分配内存并与其他进程共享内存。


编辑:摘录中提到的旁白复制如下。

旁白:为什么一旦进程退出,就不会泄漏内存

当你编写一个生存期较短的程序时,你可能会分配一些空间 用。程序运行并即将完成:是否存在 在退出之前需要打很多次电话吗?虽然看起来 错了,任何真正意义上的记忆都不会“丢失”。原因是 很简单:系统中实际上有两个级别的内存管理。 第一级内存管理由操作系统执行,操作系统 在进程运行时将内存分发给进程,并在运行时将其收回 进程退出(或以其他方式死亡)。第二级管理 在每个进程中,例如,在调用 和 时在堆中。即使你没有打电话(因此泄漏 内存),操作系统将回收 该过程(包括代码、堆栈和此处相关的页面, heap) 程序完成运行时。无论处于什么状态 在地址空间中的堆中,操作系统会收回所有这些页面 当进程死亡时,从而确保尽管 事实上,你没有释放它。malloc()free()malloc()free()free()

因此,对于生存期较短的程序,泄漏内存通常不会导致任何 操作问题(尽管它可能被认为是糟糕的形式)。什么时候 编写长时间运行的服务器(如 Web 服务器或数据库管理 系统,永远不会退出),内存泄漏是一个更大的问题, 并最终导致应用程序用完时崩溃 记忆。当然,内存泄漏是一个更大的问题 一个特定的程序:操作系统本身。向我们展示一次 再说一遍:那些编写内核代码的人的工作是最艰巨的......

内存 API 章节的第 7 页

操作系统: Three Easy Pieces
Remzi H. Arpaci-Dusseau 和 Andrea C. Arpaci-Dusseau Arpaci-Dusseau 书籍 2015年3月(版本 0.90)

2赞 Gunter Königsmann 7/27/2018 #17

如果程序在退出之前忘记释放几兆字节,操作系统将释放它们。但是,如果您的程序一次运行数周,并且程序内部的循环在每次迭代中忘记释放几个字节,那么您将产生巨大的内存泄漏,除非您定期重新启动它,否则将耗尽计算机中的所有可用内存=>如果程序用于一项非常大的任务,即使它最初不是设计的,即使是很小的内存泄漏也可能很糟糕一方面。

评论

0赞 EvilTeach 1/5/2023
+100 倍。如果你的服务器程序每次服务时都会泄漏,你最终会耗尽内存。对于一次运行程序来说,这没什么大不了的。对于 24x7 全天候运行来说,这是一件大事。从我的角度来看,如果你不分配你的资源,你就是一个没有雇员的人。
4赞 ken_you_not 10/21/2020 #18

这取决于您正在处理的项目的范围。在你的问题的上下文中,我的意思是只是你的问题,那么这并不重要。

为了进一步解释(可选),我从整个讨论中注意到的一些场景如下:

(1) - 如果你在一个嵌入式环境中工作,你不能依赖主操作系统为你回收内存,那么你应该释放它们,因为如果不注意,内存泄漏真的会使程序崩溃。

(2) - 如果你正在做一个个人项目,你不会向其他人透露它,那么你可以跳过它(假设你在主操作系统上使用它),或者为了“最佳实践”而包含它。

(3) - 如果你正在做一个项目并计划将其开源,那么你需要对你的受众进行更多的研究,并弄清楚释放内存是否是更好的选择。

(4) - 如果你有一个大型库,而你的受众只由主操作系统组成,那么你不需要释放它,因为他们的操作系统会帮助他们这样做。同时,通过不释放,您的库/程序可能有助于提高整体性能,因为程序不必关闭每个数据结构,从而延长了关闭时间(想象一下,在离开家之前关闭计算机的等待速度非常缓慢......

我可以继续指定要学习的课程,但这最终取决于你想通过你的计划实现什么。在某些情况下,释放内存被认为是好的做法,而在某些情况下则不然,因此它最终取决于您所处的具体情况以及在正确的时间提出正确的问题。祝你好运!

评论

0赞 Basile Starynkevitch 8/9/2023
一些主要的开源项目(包括 GCC 编译器)不会打扰所有动态内存。我务实地相信他们这样做是正确的(因为操作系统内核会在进程终止时恢复它)freemalloc
1赞 LHP 4/28/2022 #19

正如其他人已经指出的那样,这取决于程序运行的操作系统环境,对于长时间运行的进程,释放内存并避免即使是非常缓慢的泄漏也始终很重要。但是,如果操作系统处理的东西,就像Unix可能永远所做的那样,那么你就不需要释放内存,也不需要关闭文件(当进程退出时,内核会关闭所有打开的文件描述符。 如果您的程序分配了大量内存,那么毫不犹豫地退出甚至可能是有益的。我发现当我退出Firefox时,它会花费几分钟!在许多进程中以 GB 内存分页。我想这是由于必须在 C++ 对象上调用析构函数。这其实很可怕。有些人可能会争辩说,这对于持续保存状态是必要的,但在我看来,长时间运行的交互式程序,如浏览器、编辑器和设计程序,仅举几例,应该确保任何状态信息、首选项、打开的窗口/页面、文档等经常被写入永久存储,以避免在崩溃时丢失工作。然后,当用户选择退出时,可以再次快速执行此状态保存,完成后,进程应立即退出。

0赞 08822407d 8/1/2022 #20

为此过程分配的所有内存都将作系统标记为未使用,然后重用,因为内存分配是由用户空间函数完成的。

想象一下,OS是一个神,而记忆是创造过程的素材,神用一些素材创造了一个世界(或者说OS保留了一些内存,并在其中创造了一个进程)。无论这个世界的生物做了什么,不属于这个世界的材料都不会受到影响。这个世界过期后,神OS可以回收分配给这个世界的材料。

现代操作系统在释放用户空间内存方面可能有不同的细节,但这必须是操作系统的基本职责。

评论

0赞 reichhart 8/5/2022
问题的标志是“c malloc free”。因此,您的答案是错误的,因为可能根本没有操作系统。查看 dhein 的正确答案: stackoverflow.com/a/30118469/1950345 已经有很多答案了,你的答案没有提供新的细节。请参阅如何回答