C 程序异常终止引起的潜在内存泄漏
C potential memory leak caused by abnormally terminating program
Windows 和 Linux.
当我在 C 程序中分配内存时,良好的编码要求我在程序结束前释放内存。
假设如下:
int main (int argc, char *argv[]) {
char *f_name;
FILE *i_file;
f_name = malloc(some_amount);
// put something in f_name */
i_file = fopen(f_name, "r");
if (i_file == NULL) {
perror("Error opening file.");
exit(EXIT_FAILURE);
}
free(f_name);
return EXIT_SUCCESS;
}
如果程序在我调用 "free" 之前终止,OS 会在程序退出时恢复任何未释放的内存吗?或者我会在系统重启之前蚕食我的 3Gb 左右的可用内存吗?
谢谢,马克。
在 Windows 和 Linux 等热门 OS 上您不必担心。
虚拟内存在进程终止时不复存在。所以它不可能在进程终止后泄漏。
物理内存永远属于OS,可以随意分配,不管你的进程是否还在运行。 (除非你锁定你的分配,在这种情况下,当相应的虚拟内存映射被破坏时它不再被锁定,无论如何发生在进程终止时。)
有一些资源没有被清理(比如某些类型的共享内存),但这是非常奇特的。
当您调用 malloc
时,通常只保留后备存储(本质上是 RAM+swap)并创建虚拟内存映射(本质上是免费的)。当您第一次写入该虚拟内存映射时,内存 (RAM) 的物理页面将映射到它 "back" 它。该 RAM 始终属于 OS,可以随意使用,如果 OS 认为明智,它将重新将 RAM 用于其他目的。
当进程终止时,其地址space 不复存在。这意味着任何虚拟内存映射或保留都会消失。当虚拟内存映射消失时,未共享的物理页面的使用计数将降至零,从而释放这些物理内存页面。
值得详细了解这一点,因为如果您不了解幕后发生的事情,您很容易就边缘情况得出错误的结论。此外,这将为您提供一个框架,用于将文件映射、内存过度使用和共享内存等概念插入其中。
内存被 OS 回收。
有些程序(如网络服务器)永远不会退出,它们只是保持 运行 和服务请求。他们技术上分配的内存不需要归还。
确实,程序的内存分配会在进程终止时自动释放。但是,如果函数调用在该函数代码块中的 free()
或 delete
调用之前接收到异常,则内存不会被释放,除非它被智能指针或对象引用某种。自动释放已分配内存的一个推荐选项是使用 std::shared_ptr
,如下所示:
void BadFunction(){
char *someMemory = (char *)malloc(1024);
DoSomethingThatMakesAnException();
delete someMemory;// This never gets called!
}
void GoodFunction(){
std::shared_ptr<char> someMemory = std::shared_ptr<char>((char *)malloc(1024));
DoSomethingThatMakesAnException();
// someMemory is freed automatically, even on exception!
}
在你的例子中,采用这个分支确实会导致内存泄漏:
f_name = malloc(some_amount);
// put something in f_name */
i_file = fopen(f_name, "r");
if (i_file == NULL) {
perror("Error opening file.");
exit(EXIT_FAILURE); // <--- f_name leaks here!
}
这只是一次泄漏,在程序的整个生命周期中不会经常发生,并且常见的 OSes 会在终止时清理泄漏的内存。这不太可能成为影响系统范围性能的问题,但是 这将是 Valgrind 等工具突出显示的诊断,因此出于调试目的 是明智的 free(f_name);
在你 exit(EXIT_FAILURE);
之前。
该终止不被视为异常,因为它是由对 exit
的调用引起的。尽管如此,由对 abort
的调用或发出的信号引起的异常终止很可能会在此泄漏之上复合。
will the OS recover any un-freed memory when the program exits?
C 标准没有要求 OS 存在,更不用说它恢复未释放的内存了。一些极简主义者 OSes 可能 不会 在您之后清理。同样,如果您的程序在脚本环境(即 CGI)中运行,或者它被蚀刻到芯片中(在这种情况下您可能不希望您的程序终止),那么您以后可能会遇到问题。
Windows 和 Linux.
当我在 C 程序中分配内存时,良好的编码要求我在程序结束前释放内存。
假设如下:
int main (int argc, char *argv[]) {
char *f_name;
FILE *i_file;
f_name = malloc(some_amount);
// put something in f_name */
i_file = fopen(f_name, "r");
if (i_file == NULL) {
perror("Error opening file.");
exit(EXIT_FAILURE);
}
free(f_name);
return EXIT_SUCCESS;
}
如果程序在我调用 "free" 之前终止,OS 会在程序退出时恢复任何未释放的内存吗?或者我会在系统重启之前蚕食我的 3Gb 左右的可用内存吗?
谢谢,马克。
在 Windows 和 Linux 等热门 OS 上您不必担心。
虚拟内存在进程终止时不复存在。所以它不可能在进程终止后泄漏。
物理内存永远属于OS,可以随意分配,不管你的进程是否还在运行。 (除非你锁定你的分配,在这种情况下,当相应的虚拟内存映射被破坏时它不再被锁定,无论如何发生在进程终止时。)
有一些资源没有被清理(比如某些类型的共享内存),但这是非常奇特的。
当您调用 malloc
时,通常只保留后备存储(本质上是 RAM+swap)并创建虚拟内存映射(本质上是免费的)。当您第一次写入该虚拟内存映射时,内存 (RAM) 的物理页面将映射到它 "back" 它。该 RAM 始终属于 OS,可以随意使用,如果 OS 认为明智,它将重新将 RAM 用于其他目的。
当进程终止时,其地址space 不复存在。这意味着任何虚拟内存映射或保留都会消失。当虚拟内存映射消失时,未共享的物理页面的使用计数将降至零,从而释放这些物理内存页面。
值得详细了解这一点,因为如果您不了解幕后发生的事情,您很容易就边缘情况得出错误的结论。此外,这将为您提供一个框架,用于将文件映射、内存过度使用和共享内存等概念插入其中。
内存被 OS 回收。
有些程序(如网络服务器)永远不会退出,它们只是保持 运行 和服务请求。他们技术上分配的内存不需要归还。
确实,程序的内存分配会在进程终止时自动释放。但是,如果函数调用在该函数代码块中的 free()
或 delete
调用之前接收到异常,则内存不会被释放,除非它被智能指针或对象引用某种。自动释放已分配内存的一个推荐选项是使用 std::shared_ptr
,如下所示:
void BadFunction(){
char *someMemory = (char *)malloc(1024);
DoSomethingThatMakesAnException();
delete someMemory;// This never gets called!
}
void GoodFunction(){
std::shared_ptr<char> someMemory = std::shared_ptr<char>((char *)malloc(1024));
DoSomethingThatMakesAnException();
// someMemory is freed automatically, even on exception!
}
在你的例子中,采用这个分支确实会导致内存泄漏:
f_name = malloc(some_amount);
// put something in f_name */
i_file = fopen(f_name, "r");
if (i_file == NULL) {
perror("Error opening file.");
exit(EXIT_FAILURE); // <--- f_name leaks here!
}
这只是一次泄漏,在程序的整个生命周期中不会经常发生,并且常见的 OSes 会在终止时清理泄漏的内存。这不太可能成为影响系统范围性能的问题,但是 这将是 Valgrind 等工具突出显示的诊断,因此出于调试目的 是明智的 free(f_name);
在你 exit(EXIT_FAILURE);
之前。
该终止不被视为异常,因为它是由对 exit
的调用引起的。尽管如此,由对 abort
的调用或发出的信号引起的异常终止很可能会在此泄漏之上复合。
will the OS recover any un-freed memory when the program exits?
C 标准没有要求 OS 存在,更不用说它恢复未释放的内存了。一些极简主义者 OSes 可能 不会 在您之后清理。同样,如果您的程序在脚本环境(即 CGI)中运行,或者它被蚀刻到芯片中(在这种情况下您可能不希望您的程序终止),那么您以后可能会遇到问题。