我们都被教导必须释放每个已分配的指针。不过,我有点好奇不释放内存的真正代价。在一些明显的情况下,比如在循环内部或线程执行的一部分调用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?
在OSTEP操作系统本科课程的在线教科书中,有一个章节恰好讨论了你的问题。
相关的章节是在第6页的内存API章节中“忘记释放内存”,给出了如下解释:
在某些情况下,不调用free()似乎是合理的。为
例如,你的程序是短命的,很快就会退出;在这种情况下,
当进程死亡时,操作系统将清理它分配的所有页面
因此,内存泄漏本身不会发生。虽然这当然“有效”
(见第7页的旁白),这可能是一个坏习惯,所以要警惕
选择这样的策略
这段摘录是在介绍虚拟内存概念的上下文中。基本上,在本书的这一点上,作者解释了操作系统的目标之一是“虚拟化内存”,也就是说,让每个程序都相信它可以访问一个非常大的内存地址空间。
在幕后,操作系统会将用户看到的“虚拟地址”转换为指向物理内存的实际地址。
但是,共享物理内存等资源需要操作系统跟踪哪些进程正在使用它。因此,如果一个进程终止,那么在操作系统的能力和设计目标范围内回收该进程的内存,以便它可以重新分配并与其他进程共享内存。
编辑:节选中提到的旁白复制如下。
ASIDE: WHY NO MEMORY IS LEAKED ONCE YOUR PROCESS EXITS
When you write a short-lived program, you might allocate some space
using malloc(). The program runs and is about to complete: is there
need to call free() a bunch of times just before exiting? While it seems
wrong not to, no memory will be "lost" in any real sense. The reason is
simple: there are really two levels of memory management in the system.
The first level of memory management is performed by the OS, which
hands out memory to processes when they run, and takes it back when
processes exit (or otherwise die). The second level of management
is within each process, for example within the heap when you call
malloc() and free(). Even if you fail to call free() (and thus leak
memory in the heap), the operating system will reclaim all the memory of
the process (including those pages for code, stack, and, as relevant here,
heap) when the program is finished running. No matter what the state
of your heap in your address space, the OS takes back all of those pages
when the process dies, thus ensuring that no memory is lost despite the
fact that you didn’t free it.
Thus, for short-lived programs, leaking memory often does not cause any
operational problems (though it may be considered poor form). When
you write a long-running server (such as a web server or database management
system, which never exit), leaked memory is a much bigger issue,
and will eventually lead to a crash when the application runs out of
memory. And of course, leaking memory is an even larger issue inside
one particular program: the operating system itself. Showing us once
again: those who write the kernel code have the toughest job of all...
from Page 7 of Memory API chapter of
Operating Systems: Three Easy Pieces
Remzi H. Arpaci-Dusseau and Andrea C. Arpaci-Dusseau
Arpaci-Dusseau Books
March, 2015 (Version 0.90)
在OSTEP操作系统本科课程的在线教科书中,有一个章节恰好讨论了你的问题。
相关的章节是在第6页的内存API章节中“忘记释放内存”,给出了如下解释:
在某些情况下,不调用free()似乎是合理的。为
例如,你的程序是短命的,很快就会退出;在这种情况下,
当进程死亡时,操作系统将清理它分配的所有页面
因此,内存泄漏本身不会发生。虽然这当然“有效”
(见第7页的旁白),这可能是一个坏习惯,所以要警惕
选择这样的策略
这段摘录是在介绍虚拟内存概念的上下文中。基本上,在本书的这一点上,作者解释了操作系统的目标之一是“虚拟化内存”,也就是说,让每个程序都相信它可以访问一个非常大的内存地址空间。
在幕后,操作系统会将用户看到的“虚拟地址”转换为指向物理内存的实际地址。
但是,共享物理内存等资源需要操作系统跟踪哪些进程正在使用它。因此,如果一个进程终止,那么在操作系统的能力和设计目标范围内回收该进程的内存,以便它可以重新分配并与其他进程共享内存。
编辑:节选中提到的旁白复制如下。
ASIDE: WHY NO MEMORY IS LEAKED ONCE YOUR PROCESS EXITS
When you write a short-lived program, you might allocate some space
using malloc(). The program runs and is about to complete: is there
need to call free() a bunch of times just before exiting? While it seems
wrong not to, no memory will be "lost" in any real sense. The reason is
simple: there are really two levels of memory management in the system.
The first level of memory management is performed by the OS, which
hands out memory to processes when they run, and takes it back when
processes exit (or otherwise die). The second level of management
is within each process, for example within the heap when you call
malloc() and free(). Even if you fail to call free() (and thus leak
memory in the heap), the operating system will reclaim all the memory of
the process (including those pages for code, stack, and, as relevant here,
heap) when the program is finished running. No matter what the state
of your heap in your address space, the OS takes back all of those pages
when the process dies, thus ensuring that no memory is lost despite the
fact that you didn’t free it.
Thus, for short-lived programs, leaking memory often does not cause any
operational problems (though it may be considered poor form). When
you write a long-running server (such as a web server or database management
system, which never exit), leaked memory is a much bigger issue,
and will eventually lead to a crash when the application runs out of
memory. And of course, leaking memory is an even larger issue inside
one particular program: the operating system itself. Showing us once
again: those who write the kernel code have the toughest job of all...
from Page 7 of Memory API chapter of
Operating Systems: Three Easy Pieces
Remzi H. Arpaci-Dusseau and Andrea C. Arpaci-Dusseau
Arpaci-Dusseau Books
March, 2015 (Version 0.90)
是的,你是对的,你的例子没有造成任何伤害(至少在大多数现代操作系统上没有)。进程退出后,操作系统将恢复进程分配的所有内存。
来源:分配和GC神话(PostScript警告!)
Allocation Myth 4: Non-garbage-collected programs
should always deallocate all memory
they allocate.
The Truth: Omitted
deallocations in frequently executed
code cause growing leaks. They are
rarely acceptable. but Programs that
retain most allocated memory until
program exit often perform better
without any intervening deallocation.
Malloc is much easier to implement if
there is no free.
In most cases, deallocating memory
just before program exit is pointless.
The OS will reclaim it anyway. Free
will touch and page in the dead
objects; the OS won't.
Consequence: Be careful with "leak
detectors" that count allocations.
Some "leaks" are good!
也就是说,您应该尽量避免所有内存泄漏!
第二个问题:你的设计还可以。如果你需要存储一些东西直到你的应用程序退出,那么使用动态内存分配是可以的。如果您事先不知道所需的大小,就不能使用静态分配的内存。
我完全不同意那些说OP是正确的或没有伤害的人。
每个人都在谈论现代和/或传统的操作系统。
但是如果我在一个没有操作系统的环境中呢?
哪里什么都没有?
想象一下,现在您正在使用线程样式的中断并分配内存。
在C标准ISO/IEC:9899中,内存的寿命表示为:
7.20.3 Memory management functions
1 The order and contiguity of storage allocated by successive calls to the calloc,
malloc, and realloc functions is unspecified. The pointer returned if the allocation
succeeds is suitably aligned so that it may be assigned to a pointer to any type of object
and then used to access such an object or an array of such objects in the space allocated
(until the space is explicitly deallocated). The lifetime of an allocated object extends
from the allocation until the deallocation.[...]
所以我们不能认为环境在为你做释放的工作。
否则,它将被添加到最后一句:“或直到程序终止。”
换句话说:
不释放内存不仅仅是不好的做法。它生成不可移植且不符合C的代码。
至少可以被视为“正确的”,如果有以下情况:[…]],由environment支持。
但如果你根本就没有操作系统,那就没有人在为你做这项工作了
(我知道通常在嵌入式系统中不会分配和重新分配内存,
但在某些情况下,你可能想这么做。)
在普通的C语言中(OP被标记)
这只会产生错误的和不可移植的代码。