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

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

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

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

Second, let's say I have a program that acts a bit like a shell. Users can declare variables like aaa = 123 and those are stored in some dynamic data structure for later use. Clearly, it seems obvious that you'd use some solution that will calls some *alloc function (hashmap, linked list, something like that). For this kind of program, it doesn't make sense to ever free after calling malloc because these variables must be present at all times during the program's execution and there's no good way (that I can see) to implement this with statically allocated space. Is it bad design to have a bunch of memory that's allocated but only freed as part of the process ending? If so, what's the alternative?


当前回答

如果您正在从头开始开发一个应用程序,那么您可以在何时调用free方面做出一些明智的选择。您的示例程序很好:它分配内存,也许您让它工作几秒钟,然后关闭,释放它所要求的所有资源。

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

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

其他回答

===将来的校对和代码重用怎么办?= = =

如果你不编写释放对象的代码,那么你就将代码限制为只有当你可以依赖于进程关闭释放内存时才能安全使用……例如,小型一次性使用项目或“一次性”[1]项目)……你知道这个过程什么时候结束。

如果您确实编写了free()释放所有动态分配内存的代码,那么您就可以在未来验证代码,并让其他人在更大的项目中使用它。


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

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

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

你可能写了一些没有释放分配内存的代码片段,它以这样一种方式运行,然后自动回收内存。似乎还好吧。

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

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

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

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

关于你的第二个问题,如果我的程序输入a=5,我会为a分配空间,或者在后续的a="foo"上重新分配相同的空间。这笔款项将继续分配至:

用户输入'unset a' 我的清理功能被输入,要么服务一个信号,要么用户输入“退出”

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

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

char *t;

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

如果这发生在fork / daemonizing(理论上永远运行)之前,那么您的程序已经泄漏了255次大小不确定的t。

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

真的,当你去做其他事情的时候,要善待那些不得不维护你的东西的可怜人。递给他们“valgrind clean”:)

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

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

这取决于你正在做的项目的范围。在你的问题的背景下,我是说仅仅是你的问题,那就不重要了。

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

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

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

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

(4)如果你有一个很大的库,而你的用户只有主操作系统,那么你就不需要释放它,因为他们的操作系统会帮助他们这么做。同时,通过不释放,你的库/程序可能有助于提高整体性能,因为程序不需要关闭每个数据结构,延长关机时间(想象一下,在离开家之前,你要非常缓慢地等待关机……)

我可以继续指定要采取哪一种方法,但这最终取决于您想用程序实现什么。释放内存在某些情况下被认为是很好的实践,但在某些情况下则不是,因此它最终取决于您所处的具体情况,以及在正确的时间提出正确的问题。好运!