ChatGPT解决这个技术问题 Extra ChatGPT

当您在程序终止之前在 malloc 之后不释放时,真正会发生什么?

我们都被教导必须释放每个分配的指针。不过,我有点好奇不释放内存的实际成本。在某些明显的情况下,例如在循环或线程执行的一部分中调用 malloc() 时,释放非常重要,因此不会出现内存泄漏。但请考虑以下两个示例:

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

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

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

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

下面的人一直在说一个好的现代操作系统会进行清理,但是如果代码在内核模式下运行(例如,出于性能原因)怎么办?内核模式程序(例如在 Linux 中)是沙盒的吗?如果没有,我相信你需要手动释放所有东西,我想,甚至在任何异常终止之前,比如使用 abort()。
@Dr.PersonPersonII 是的,在内核模式下运行的代码通常必须手动释放所有内容。
我想补充一点,free(a) 实际上并没有真正释放内存!它只是重置了 malloc 的 libc 实现中的一些指针,这些指针跟踪一个大的 mmaped 内存页面(通常称为“堆”)内的可用内存块。该页面仍然只会在您的程序终止时被释放,而不是之前。
@MarcoBonelli 部分正确。如果 malloc() 的内存来自“正常”的 sbrk 堆,并且在其末尾,则调用 sbrk() 以减少内存映像。如果 malloc() 通过 mmap() 分配内存,则它在 free() 中未映射。
Free() 可能会或可能不会实际释放内存。它可能只是将块标记为已释放,稍后将被回收,或者可能将其链接到空闲列表中。它可能会将其合并到相邻的空闲块中,或者可能会将其留给后续分配。这都是一个实现细节。

P
Paul Tomblin

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

因此,一般来说,它没有任何害处,除了存储空间超过您需要的运行时间成本。当然,在您给出的示例中,您希望保留可能使用的变量的内存,直到它被清除。

但是,当您不再需要它时立即释放内存并在程序退出时释放您仍然存在的任何东西,这被认为是一种很好的风格。这更像是一种了解您正在使用的内存的练习,并考虑您是否仍然需要它。如果你不跟踪,你可能有内存泄漏。

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


可能还好提到不是每个人都在使用现代操作系统,如果有人使用您的程序(并且它仍然在不恢复内存的操作系统上运行)运行它,那么 GG。
我真的认为这个答案是错误的。一个人应该总是在完成资源后释放资源,无论是文件句柄/内存/互斥体。有了这个习惯,在构建服务器时就不会犯这种错误。一些服务器预计将 24x7 运行。在这些情况下,任何形式的泄漏都意味着您的服务器最终将耗尽该资源并以某种方式挂起/崩溃。一个简短的实用程序,你的泄漏并不是那么糟糕。任何服务器,任何泄漏都是死亡。帮自己一个忙。自己打扫干净。这是一个好习惯。
“但是,当您不再需要它时立即释放内存并在程序退出时释放您仍然存在的任何东西,这被认为是一种很好的风格。”那你认为错了吗?
如果您有一个直到程序退出时都需要的内存存储,并且您没有在原始操作系统上运行,那么在退出之前释放内存是一种风格选择,而不是缺陷。
@Paul——仅仅同意EvilTeach,不被认为是释放内存的好风格,不释放内存是不正确的。您的措辞使这看起来与佩戴与您的领带相配的手帕一样重要。其实就是穿裤子的水平。
C
Csq

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

来源:Allocation and GC Myths(PostScript 警报!)

分配误区 4:非垃圾回收程序应始终释放它们分配的所有内存。真相:在频繁执行的代码中省略释放会导致越来越多的泄漏。他们很少被接受。但是在程序退出之前保留大部分分配内存的程序通常在没有任何干预解除分配的情况下执行得更好。如果没有免费的,Malloc 更容易实现。在大多数情况下,在程序退出之前释放内存是没有意义的。无论如何,操作系统都会收回它。自由会在死物中触摸和翻页;操作系统不会。结果:小心计算分配的“泄漏检测器”。一些“泄漏”是好的!

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

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


可能是因为我读到的问题是泄漏的内存实际发生了什么,而不是这个特定示例是否可以。不过我不会投反对票,因为它仍然是一个很好的答案。
可能存在(早期的 Windows,早期的 Mac OS),并且可能仍然存在需要进程在退出之前释放内存的操作系统,否则不会回收空间。
没关系,除非您关心内存碎片或内存不足 - 您这样做太多,您的应用程序性能将消失。除了确凿的事实,始终遵循最佳实践和良好习惯的养成。
我认为通过说“因为泄漏检测器”来解释需要释放内存是错误的。这就像在说“你必须在游戏街上慢慢开车,因为警察可能会拿着测速相机在等你”。
事实上,即使在一个长期运行的程序中,一次性的中等大小的泄漏也不是问题。 (强调“一次性”部分。)但是,清理它仍然是最佳实践,这样验证器就不会抱怨——不是因为关闭验证器本身就很有用,而是因为如果你在您的验证输出中有一堆“可接受的”失败,那么很难找到不可接受的失败。
T
Trevor Boyd Smith

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

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

如果您确实编写了释放()所有动态分配的内存的代码,那么您将在未来对代码进行校对并让其他人在更大的项目中使用它。

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

我听说一个人写了一些代码只是为了好玩,以使他的硬件更好地工作。他说“just a hobby, won't be big and professional”。多年后,很多人都在使用他的“爱好”代码。


对“小项目”投了反对票。有许多大型项目在退出时故意不释放内存,因为如果您知道目标平台,那是浪费时间。 IMO,一个更准确的例子是“孤立的项目”。例如,如果您正在创建一个将包含在其他应用程序中的可重用库,则没有明确定义的退出点,因此您不应该泄漏内存。对于独立应用程序,您将始终准确地知道进程何时结束,并且可以有意识地决定将清理工作卸载到操作系统(无论哪种方式都必须进行检查)。
昨天的应用程序是今天的库函数,明天它会被链接到一个长寿命的服务器上,调用它数千次。
@AdrianMcCarthy:如果一个函数检查静态指针是否为空,如果是则用 malloc() 初始化它,如果指针仍然为空则终止,即使 free 也可以安全地使用这样的函数任意次数永远不会被调用。我认为区分可能耗尽无限量存储的内存泄漏与只能浪费有限且可预测的存储量的情况可能是值得的。
@supercat:我的评论是关于随时间变化的代码。当然,泄漏有限的内存不是问题。但是有一天,有人会想要改变那个函数,使它不再使用静态指针。如果代码没有规定能够释放指向的内存,那将是一个硬更改(或者,更糟糕的是,更改将是糟糕的,最终会导致无限泄漏)。
@AdrianMcCarthy:将代码更改为不再使用静态指针可能需要将指针移动到某种“上下文”对象中,并添加代码来创建和销毁此类对象。如果指针在不存在分配的情况下始终为 null,并且在存在分配时为非空,则在销毁上下文时让代码释放分配并将指针设置为 null 将很简单,尤其是与其他所有内容相比这需要将静态对象移动到上下文结构中。
D
DigitalRoss

你是对的,没有伤害,退出速度更快

这有多种原因:

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

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

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

关于未来代码重用的可能性,证明无意义操作的确定性:这是一个考虑因素,但可以说不是Agile方式。 YAGNI!


我曾经做过一个项目,我们花了很短的时间试图了解程序的内存使用情况(我们需要支持它,我们没有编写它)。根据我的经验,我同意你的第二个子弹。但是,我想听听您(或某人)提供更多证据证明这是真的。
没关系,找到答案:stackoverflow.com/questions/1421491/…。谢谢你这么!
YAGNI 原则是双向的:你永远不需要优化关闭路径。过早的优化等等。
您可能会提到,在退出之前释放内存不仅浪费运行时间,而且浪费开发人员和潜在的测试时间。
“敏捷”的论点是错误的。这也是敏捷的:给定两个或更多大致相同的选项,选择使未来更改更容易的路径。这也可能是在 exit() 之前调用 free() 的路径:当程序发展时,exit() 不一定保留在程序的末尾。因此,“敏捷”论点是“视情况而定”。
C
Community

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

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

但是,如果我在一个根本没有操作系统的环境中呢?什么地方没有?

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

7.20.3 内存管理函数 1 连续调用 calloc、malloc 和 realloc 函数分配的存储顺序和连续性是未指定的。如果分配成功,则返回的指针经过适当对齐,以便可以将其分配给指向任何类型对象的指针,然后用于访问已分配空间中的此类对象或此类对象的数组(直到空间被显式释放) .已分配对象的生命周期从分配延伸到解除分配。[...]

因此,不必认为环境正在为您完成释放工作。否则它将被添加到最后一句:“或者直到程序终止。”

换句话说:不释放内存不仅仅是不好的做法。它产生不可移植且不符合 C 的代码。至少可以将其视为“正确的,如果以下内容:[...],受环境支持”。

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

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


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

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

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

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

用户输入了“unset a”我的清理功能被输入,服务信号或用户输入“退出”

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

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

char *t;

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

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

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

真的,善待可怜的灵魂,当你继续做其他事情时,他们必须维护你的东西..把它交给他们'valgrind clean':)


free() is cheap 除非您有 10 亿个具有复杂关系的数据结构必须一个一个释放,否则遍历数据结构以尝试释放所有内容可能最终会显着增加您的停机时间,尤其是如果该数据结构的一半是已经分页到磁盘,没有任何好处。
@LieRyan如果你有十亿,就像字面上的十亿结构一样,你肯定还有其他需要专门考虑的问题 - 远远超出了这个特定答案的范围:)
A
Antti Huima

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

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


Valgrind 在区分“泄露”和“仍然可以访问”方面做得不是很好吗?
-1 表示“完全没问题” 留下分配的内存而不释放它是不好的编码习惯。如果该代码被提取到库中,那么它将导致整个地方的内存泄漏。
+1 补偿。请参阅compie的答案。 freeexit 时间被认为是有害的。
B
Bill the Lizard

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

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


W
WedaPashi

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

您可能已经编写了一些不释放分配的内存的代码片段,它以这样的方式运行,然后自动回收内存。似乎没事。

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

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


值得注意的是,不仅有人复制代码片段的可能性,而且还有编写用于执行某些特定操作的程序一旦被修改为重复执行该操作的可能性。在这种情况下,将内存分配一次然后重复使用而不被释放是可以的,但是为每个操作分配和放弃内存(而不释放它)可能是灾难性的。
D
DevSolar

这里的真正结果是什么?

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

大多数现代桌面操作系统确实会在进程终止时恢复泄漏的内存,因此很遗憾地忽略了这个问题(正如这里的许多其他答案所看到的那样。)

但是您依赖的是您不应该依赖的安全功能,并且您的程序(或函数)可能在下一次这种行为确实导致“硬”内存泄漏的系统上运行。

您可能在内核模式下运行,或者在不采用内存保护作为权衡的老式/嵌入式操作系统上运行。 (MMU占用die空间,内存保护需要额外的CPU周期,要求程序员自己清理也不算多)。

您可以以任何您喜欢的方式使用和重用内存,但请确保在退出之前释放所有资源。


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

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

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

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

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

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

但是,共享物理内存等资源需要操作系统跟踪正在使用它的进程。因此,如果一个进程终止,那么操作系统的能力和设计目标就是回收该进程的内存,以便它可以重新分配并与其他进程共享内存。

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

旁白:为什么一旦你的进程退出就没有内存泄漏当你编写一个短暂的程序时,你可能会使用 malloc() 分配一些空间。程序运行并即将完成:在退出之前是否需要多次调用 free() ?虽然不这样做似乎是错误的,但在任何真正意义上都不会“丢失”任何记忆。原因很简单:系统中确实有两个级别的内存管理。第一级内存管理由操作系统执行,它在进程运行时将内存分配给进程,并在进程退出(或以其他方式死亡)时收回。第二级管理在每个进程内,例如在调用 malloc() 和 free() 时在堆内。即使您未能调用 free()(因此在堆中泄漏内存),操作系统也会在程序执行时回收进程的所有内存(包括代码页、堆栈以及此处相关的堆)运行完毕。无论您的地址空间中的堆处于何种状态,操作系统都会在进程终止时收回所有这些页面,从而确保即使您没有释放内存也不会丢失内存。因此,对于短期程序,内存泄漏通常不会导致任何操作问题(尽管它可能被认为是不良形式)。当您编写一个长时间运行的服务器(例如永远不会退出的 Web 服务器或数据库管理系统)时,内存泄漏是一个更大的问题,并且最终会在应用程序耗尽内存时导致崩溃。当然,内存泄漏是一个特定程序内部的一个更大的问题:操作系统本身。再次向我们展示:编写内核代码的人是所有工作中最艰巨的工作......来自操作系统的内存 API 章节的第 7 页:三个简单的部分 Remzi H. Arpaci-Dusseau 和 Andrea C. Arpaci-Dusseau Arpaci-Dusseau图书 2015 年 3 月(0.90 版)


K
Kyle Cronin

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

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


对于您的第一个陈述“没有危险”,我想说-1,只是您随后就为什么存在危险给出了深思熟虑的答案。
随着危险的发生,它是非常良性的——我随时都会因段错误而发生内存泄漏。
非常正确,我们俩都不喜欢=D
@KyleCronin我宁愿有段错误而不是内存泄漏,因为两者都是严重的错误,段错误更容易检测。内存泄漏经常被忽视或无法解决,因为它们“非常良性”。我的 RAM 和我完全不同意。
@Dan 作为开发人员,当然。作为用户,我将处理内存泄漏。我宁愿拥有可以工作的软件,尽管会泄漏内存,而不是不能工作的软件。
M
Michael

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


U
Uri

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

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

话虽如此,在大多数程序中,这并不是一个真正的选择,否则它会导致您耗尽内存。


o
ojrac

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

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

暂时忽略务实的一面,遵循更严格的方法会更安全,并强迫自己释放所有你 malloc 的东西。如果您没有在编写代码时观察内存泄漏的习惯,那么您很容易就会出现一些泄漏。所以换句话说,是的——没有它你可以逃脱;不过请小心。


k
ken_you_not

这取决于您正在从事的项目的范围。在你的问题的背景下,我的意思是你的问题,那么没关系。

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

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

(2) - 如果您正在从事一个不会向其他人透露的个人项目,那么您可以跳过它(假设您在主操作系统上使用它)或将其包含在“最佳实践”中清酒。

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

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

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


G
Gunter Königsmann

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


L
LHP

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


m
mouviciel

我认为您的两个示例实际上只是一个:free() 应该仅在流程结束时出现,正如您指出的那样,由于流程正在终止,所以这没有用。

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