我们都被教导必须释放每个已分配的指针。不过,我有点好奇不释放内存的真正代价。在一些明显的情况下,比如在循环内部或线程执行的一部分调用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?


当前回答

在那方面你完全正确。在小的程序中,变量必须存在,直到程序结束,释放内存并没有真正的好处。

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

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

其他回答

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

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

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

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

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

所以一般来说,这样做没有什么坏处,除了存储容量超过所需的运行时成本。当然,在您给出的示例中,您希望保留可能被使用的变量的内存,直到它被清除。

然而,一旦你不再需要内存,就立即释放内存,并在程序退出时释放你仍然拥有的内存,这被认为是一种很好的方式。它更像是一种了解您正在使用哪些内存的练习,并考虑是否仍然需要它。如果不进行跟踪,可能会出现内存泄漏。

On the other hand, the similar admonition to close your files on exit has a much more concrete result - if you don't, the data you wrote to them might not get flushed, or if they're a temp file, they might not get deleted when you're done. Also, database handles should have their transactions committed and then closed when you're done with them. Similarly, if you're using an object oriented language like C++ or Objective C, not freeing an object when you're done with it will mean the destructor will never get called, and any resources the class is responsible might not get cleaned up.

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

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

一旦我确定我已经完成了每个分配的块,我通常会释放它。今天,我的程序的入口点可能是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”:)