堆内存分配
Heap memory allocation
如果我在我的程序中使用 malloc()
动态分配内存,但我在程序运行时没有释放内存,那么动态分配的内存会在程序终止后被释放吗?
或者如果没有释放,我一遍又一遍地执行同一个程序,它会不会每次都分配不同的内存块?如果是这样,我应该如何释放该内存?
注意:我能想到的一个答案是重新启动我正在执行程序的机器。但是如果我在远程机器上执行程序并且重启不是一个选项?
程序终止时应释放程序分配的任何内存,无论它是静态分配还是动态分配。主要的例外是如果进程被分叉到另一个进程。
如果您没有明确 free
您 malloc
的任何内存,它将保持分配状态,直到进程终止。
简短回答:一旦您的进程终止,任何合理的操作系统都会释放该进程分配的所有内存。所以不,当您多次重新启动进程时,内存分配不会累积。
进程和内存管理通常是操作系统的责任,因此在进程终止后是否释放分配的内存实际上取决于操作系统。不同的操作系统可以不同地处理内存管理。
话虽这么说,任何合理的操作系统(尤其是多任务操作系统)都会释放进程终止后分配的所有内存。
我认为这背后的原因是操作系统必须能够优雅地处理不规则情况:
- 恶意程序(例如那些不故意释放内存的程序,希望影响它们 运行 所在的系统)
- 程序异常终止(即程序意外结束的情况,因此可能没有机会显式
free
其动态分配的内存本身)
任何称职的操作系统都必须能够处理这种情况。它必须将系统的其他部分(例如它自己和其他 运行ning 进程)与故障进程隔离开来。否则,进程的内存泄漏将传播到系统。这意味着 OS 会泄漏内存(这通常被认为是一个错误)。
保护系统免受内存泄漏的一种方法是确保一旦进程结束,它使用的所有内存(以及可能的其他资源)都会被释放。
由 malloc 分配的内存需要通过分配 program.If 释放,并且内存一直在分配,然后有一点表明程序将 运行 超出允许的内存分配并且抛出分段或内存不足错误。 malloc每组内存分配都需要free。
即使您的 OS 在 exit()
上进行了清理。退出的系统调用通常由 exit()
函数包装。这是一些伪代码,源自研究几个 libc 实现,以演示在 main()
周围发生的可能导致问题的情况。
//unfortunately gcc has no builtin for stack pointer, so we use assembly
#ifdef __x86_64__
#define STACK_POINTER "rsp"
#elif defined __i386__
#define STACK_POINTER "esp"
#elif defined __aarch64__
#define STACK_POINTER "x13"
#elif defined __arm__
#define STACK_POINTER "r13"
#else
#define STACK_POINTER "sp" //most commonly used name on other arches
#endif
char **environ;
void exit(int);
int main(int,char**,char**);
_Noreturn void _start(void){
register long *sp __asm__( STACK_POINTER );
//if you don't use argc, argv or envp/environ, just remove them
long argc = *sp;
char **argv = (char **)(sp + 1);
environ = (char **)(sp + argc + 1);
//init routines for threads, dynamic linker, etc... go here
exit(main((int)argc, argv, environ));
__builtin_unreachable(); //or for(;;); to shut up compiler warnings
}
请注意,exit 是使用 main 的 return 值调用的。在没有动态链接器或线程的静态构建中,exit()
可以是直接内联的 syscall(__NR_exit,main(...))
;但是,如果您的 libc 使用 exit()
的包装器执行 *_fini()
例程(大多数 libc 实现都这样做),则在 main()
终止后仍然有 1 个函数要调用。
恶意程序可以 LD_PRELOAD exit()
或其调用的任何例程,并将其变成一种永远不会释放其内存的僵尸进程。
即使您在 exit()
之前执行 free()
进程仍会消耗一些内存(基本上是可执行文件的大小,并且在某种程度上是其他人未使用的共享库进程),但一些操作系统可以重新使用非 malloc()
ed 内存用于同一程序的后续加载,这样您就可以 运行 数月而不会注意到僵尸。
FWIW,大多数 libc 实现确实有某种 exit()
包装器,但 dietlibc(当作为静态库构建时)和我的部分静态 libc.h 除外仅在 Puppy Linux 论坛上发布。
正如我们所说,操作系统的大脑是内核。操作系统有几个职责。
内存管理是内核的一个功能。
Kernel has full access to the system's memory and must allow processes
to safely access this memory as they require it.
通常这样做的第一步是虚拟寻址,通常通过分页 and/or 分段来实现。虚拟寻址允许内核使给定的物理地址看起来是另一个地址,即虚拟地址。不同进程的虚拟地址空间可能不同;一个进程在特定(虚拟)地址访问的内存可能与另一个进程在同一地址访问的内存不同。
This allows every program to behave as if it is the only one (apart
from the kernel) running and thus prevents applications from crashing
each other
malloc
Allocate block of memory from heap
。 .NET 等效项:不适用。要调用标准 C 函数,请使用 PInvoke。
The Heap
The heap is a region of your computer's memory that is not managed
automatically for you, and is not as tightly managed by the CPU. It is
a more free-floating region of memory (and is larger). To allocate
memory on the heap, you must use malloc()
or calloc()
, which are
built-in C functions. Once you have allocated memory on the heap, you
are responsible for using free()
to deallocate that memory once you
don't need it any more. If you fail to do this, your program will have
what is known as a memory leak. That is, memory on the heap will
still be set aside (and won't be available to other processes).
内存泄漏
当进程从分页或非分页池中分配内存但不释放内存时,就会发生内存泄漏。结果,这些有限的内存池会随着时间的推移而耗尽,导致 Windows 速度变慢。如果内存完全耗尽,可能会导致失败。
Determining Whether a Leak Exists 描述了一种您可以使用的技巧
如果您不确定您的系统是否存在内存泄漏。
Finding a Kernel-Mode Memory Leak 描述了如何查找泄漏
由内核模式驱动程序或组件引起。
Finding a User-Mode Memory Leak 描述了如何找到泄漏点
由用户模式驱动程序或应用程序引起。
Preventing Memory Leaks in Windows Applications
内存泄漏是一种 class 错误,其中应用程序在不再需要时无法释放内存。随着时间的推移,内存泄漏会影响特定应用程序和操作系统的性能。由于过多的分页,大量泄漏可能会导致无法接受的响应时间。最终,应用程序以及操作系统的其他部分都会出现故障。
Windows will free all memory allocated by the application on process
termination, so short-running applications will not affect overall
system performance significantly. However, leaks in long-running
processes like services or even Explorer plug-ins can greatly impact
system reliability and might force the user to reboot Windows in order
to make the system usable again.
应用程序可以通过多种方式为其分配内存。如果在使用后未释放,每种类型的分配都可能导致泄漏
。以下是常见分配模式的一些示例:
堆内存通过HeapAlloc
函数或其C/C++运行时
等价物 malloc
或 new
通过 VirtualAlloc
从操作系统直接分配
函数.
通过 Kernel32 API 创建的内核句柄,例如 CreateFile
,
CreateEvent
,或CreateThread
,代表内核内存
申请
通过 User32 和 Gdi32 API 创建的 GDI 和 USER 句柄(默认情况下,
每个进程有 10,000 个句柄的配额)
memprof 是一个分析内存使用情况和查找内存泄漏的工具。
它可以生成一个配置文件,每个配置文件分配了多少内存
在你的程序中运行。此外,它还可以扫描内存并查找块
您已分配但不再在任何地方引用。
If I allocate memory dynamically in my program using malloc() but I
don't free the memory during program runtime, will the dynamically
allocated memory be freed after program terminates?
操作系统会释放通过malloc分配的内存,供其他系统使用。
这比您的问题听起来要复杂得多,因为进程使用的物理内存可能会写入磁盘(页面调出)。但是对于 Windows, Unix (Linux, MAC OS X, iOS, android) 系统将释放它已经提交的资源到流程。
Or if it is not freed, and I execute the same program over and over
again, will it allocate the different block of memory every time? If
that is the case, how should I free that memory?
每次启动程序,都会获得一组新的内存。这是从系统中获取的,并作为虚拟地址提供。现代操作系统使用 address-space-layout-randomization (ASLR) 作为安全功能,这意味着堆应该在每次程序启动时提供唯一地址。但是由于其他运行的资源已经整理完毕,所以没有必要释放那块内存。
正如您所指出的,如果后续 运行 跟踪它已提交的资源,它如何才能释放它们。
另请注意,您可以 运行 您的程序同时多次启动 运行。分配的内存可能看起来重叠 - 每个程序可能会看到分配的相同地址,但那是 "virtual memory" - 操作系统已独立设置每个进程,因此它看起来使用相同的内存,但与每个进程关联的 RAM过程将是独立的。
程序执行时不释放内存 "work" 在 Windows 和 Unix 上,可能还有任何其他合理的操作系统。
不释放内存的好处
操作系统保留分配给进程的大内存块列表,malloc 库也保留分配给 malloc 的小内存块表。
通过不释放内存,您将在进程终止时节省对这些小列表的统计工作。在某些情况下甚至建议这样做(例如 MSDN : Service Control Handler 建议 SERVICE_CONTROL_SHUTDOWN 应该通过不释放内存来处理)
不释放内存的缺点
valgrind 和应用程序验证程序等程序通过监视分配给进程的内存并报告泄漏来检查程序的正确性。
当您不释放内存时,这些将报告大量噪音,从而难以发现无意的泄漏。如果您在循环内泄漏内存,这将很重要,这将限制您的程序可以交付的任务的大小。
在我的职业生涯中,我曾多次将进程转换为共享进程 object/dll。这些是有问题的转换,因为预期由 OS 进程终止处理的泄漏开始在 "main".
的生命周期之后继续存在。
如果我在我的程序中使用 malloc()
动态分配内存,但我在程序运行时没有释放内存,那么动态分配的内存会在程序终止后被释放吗?
或者如果没有释放,我一遍又一遍地执行同一个程序,它会不会每次都分配不同的内存块?如果是这样,我应该如何释放该内存?
注意:我能想到的一个答案是重新启动我正在执行程序的机器。但是如果我在远程机器上执行程序并且重启不是一个选项?
程序终止时应释放程序分配的任何内存,无论它是静态分配还是动态分配。主要的例外是如果进程被分叉到另一个进程。
如果您没有明确 free
您 malloc
的任何内存,它将保持分配状态,直到进程终止。
简短回答:一旦您的进程终止,任何合理的操作系统都会释放该进程分配的所有内存。所以不,当您多次重新启动进程时,内存分配不会累积。
进程和内存管理通常是操作系统的责任,因此在进程终止后是否释放分配的内存实际上取决于操作系统。不同的操作系统可以不同地处理内存管理。
话虽这么说,任何合理的操作系统(尤其是多任务操作系统)都会释放进程终止后分配的所有内存。
我认为这背后的原因是操作系统必须能够优雅地处理不规则情况:
- 恶意程序(例如那些不故意释放内存的程序,希望影响它们 运行 所在的系统)
- 程序异常终止(即程序意外结束的情况,因此可能没有机会显式
free
其动态分配的内存本身)
任何称职的操作系统都必须能够处理这种情况。它必须将系统的其他部分(例如它自己和其他 运行ning 进程)与故障进程隔离开来。否则,进程的内存泄漏将传播到系统。这意味着 OS 会泄漏内存(这通常被认为是一个错误)。
保护系统免受内存泄漏的一种方法是确保一旦进程结束,它使用的所有内存(以及可能的其他资源)都会被释放。
由 malloc 分配的内存需要通过分配 program.If 释放,并且内存一直在分配,然后有一点表明程序将 运行 超出允许的内存分配并且抛出分段或内存不足错误。 malloc每组内存分配都需要free。
即使您的 OS 在 exit()
上进行了清理。退出的系统调用通常由 exit()
函数包装。这是一些伪代码,源自研究几个 libc 实现,以演示在 main()
周围发生的可能导致问题的情况。
//unfortunately gcc has no builtin for stack pointer, so we use assembly
#ifdef __x86_64__
#define STACK_POINTER "rsp"
#elif defined __i386__
#define STACK_POINTER "esp"
#elif defined __aarch64__
#define STACK_POINTER "x13"
#elif defined __arm__
#define STACK_POINTER "r13"
#else
#define STACK_POINTER "sp" //most commonly used name on other arches
#endif
char **environ;
void exit(int);
int main(int,char**,char**);
_Noreturn void _start(void){
register long *sp __asm__( STACK_POINTER );
//if you don't use argc, argv or envp/environ, just remove them
long argc = *sp;
char **argv = (char **)(sp + 1);
environ = (char **)(sp + argc + 1);
//init routines for threads, dynamic linker, etc... go here
exit(main((int)argc, argv, environ));
__builtin_unreachable(); //or for(;;); to shut up compiler warnings
}
请注意,exit 是使用 main 的 return 值调用的。在没有动态链接器或线程的静态构建中,exit()
可以是直接内联的 syscall(__NR_exit,main(...))
;但是,如果您的 libc 使用 exit()
的包装器执行 *_fini()
例程(大多数 libc 实现都这样做),则在 main()
终止后仍然有 1 个函数要调用。
恶意程序可以 LD_PRELOAD exit()
或其调用的任何例程,并将其变成一种永远不会释放其内存的僵尸进程。
即使您在 exit()
之前执行 free()
进程仍会消耗一些内存(基本上是可执行文件的大小,并且在某种程度上是其他人未使用的共享库进程),但一些操作系统可以重新使用非 malloc()
ed 内存用于同一程序的后续加载,这样您就可以 运行 数月而不会注意到僵尸。
FWIW,大多数 libc 实现确实有某种 exit()
包装器,但 dietlibc(当作为静态库构建时)和我的部分静态 libc.h 除外仅在 Puppy Linux 论坛上发布。
正如我们所说,操作系统的大脑是内核。操作系统有几个职责。
内存管理是内核的一个功能。
Kernel has full access to the system's memory and must allow processes to safely access this memory as they require it.
通常这样做的第一步是虚拟寻址,通常通过分页 and/or 分段来实现。虚拟寻址允许内核使给定的物理地址看起来是另一个地址,即虚拟地址。不同进程的虚拟地址空间可能不同;一个进程在特定(虚拟)地址访问的内存可能与另一个进程在同一地址访问的内存不同。
This allows every program to behave as if it is the only one (apart from the kernel) running and thus prevents applications from crashing each other
malloc
Allocate block of memory from heap
。 .NET 等效项:不适用。要调用标准 C 函数,请使用 PInvoke。
The Heap
The heap is a region of your computer's memory that is not managed automatically for you, and is not as tightly managed by the CPU. It is a more free-floating region of memory (and is larger). To allocate memory on the heap, you must use
malloc()
orcalloc()
, which are built-in C functions. Once you have allocated memory on the heap, you are responsible for usingfree()
to deallocate that memory once you don't need it any more. If you fail to do this, your program will have what is known as a memory leak. That is, memory on the heap will still be set aside (and won't be available to other processes).
内存泄漏
当进程从分页或非分页池中分配内存但不释放内存时,就会发生内存泄漏。结果,这些有限的内存池会随着时间的推移而耗尽,导致 Windows 速度变慢。如果内存完全耗尽,可能会导致失败。
Determining Whether a Leak Exists 描述了一种您可以使用的技巧 如果您不确定您的系统是否存在内存泄漏。
Finding a Kernel-Mode Memory Leak 描述了如何查找泄漏 由内核模式驱动程序或组件引起。
Finding a User-Mode Memory Leak 描述了如何找到泄漏点 由用户模式驱动程序或应用程序引起。
Preventing Memory Leaks in Windows Applications
内存泄漏是一种 class 错误,其中应用程序在不再需要时无法释放内存。随着时间的推移,内存泄漏会影响特定应用程序和操作系统的性能。由于过多的分页,大量泄漏可能会导致无法接受的响应时间。最终,应用程序以及操作系统的其他部分都会出现故障。
Windows will free all memory allocated by the application on process termination, so short-running applications will not affect overall system performance significantly. However, leaks in long-running processes like services or even Explorer plug-ins can greatly impact system reliability and might force the user to reboot Windows in order to make the system usable again.
应用程序可以通过多种方式为其分配内存。如果在使用后未释放,每种类型的分配都可能导致泄漏
。以下是常见分配模式的一些示例:
堆内存通过
HeapAlloc
函数或其C/C++运行时 等价物malloc
或new
通过
VirtualAlloc
从操作系统直接分配 函数.通过 Kernel32 API 创建的内核句柄,例如
CreateFile
,CreateEvent
,或CreateThread
,代表内核内存 申请通过 User32 和 Gdi32 API 创建的 GDI 和 USER 句柄(默认情况下, 每个进程有 10,000 个句柄的配额)
memprof 是一个分析内存使用情况和查找内存泄漏的工具。 它可以生成一个配置文件,每个配置文件分配了多少内存 在你的程序中运行。此外,它还可以扫描内存并查找块 您已分配但不再在任何地方引用。
If I allocate memory dynamically in my program using malloc() but I don't free the memory during program runtime, will the dynamically allocated memory be freed after program terminates?
操作系统会释放通过malloc分配的内存,供其他系统使用。
这比您的问题听起来要复杂得多,因为进程使用的物理内存可能会写入磁盘(页面调出)。但是对于 Windows, Unix (Linux, MAC OS X, iOS, android) 系统将释放它已经提交的资源到流程。
Or if it is not freed, and I execute the same program over and over again, will it allocate the different block of memory every time? If that is the case, how should I free that memory?
每次启动程序,都会获得一组新的内存。这是从系统中获取的,并作为虚拟地址提供。现代操作系统使用 address-space-layout-randomization (ASLR) 作为安全功能,这意味着堆应该在每次程序启动时提供唯一地址。但是由于其他运行的资源已经整理完毕,所以没有必要释放那块内存。
正如您所指出的,如果后续 运行 跟踪它已提交的资源,它如何才能释放它们。
另请注意,您可以 运行 您的程序同时多次启动 运行。分配的内存可能看起来重叠 - 每个程序可能会看到分配的相同地址,但那是 "virtual memory" - 操作系统已独立设置每个进程,因此它看起来使用相同的内存,但与每个进程关联的 RAM过程将是独立的。
程序执行时不释放内存 "work" 在 Windows 和 Unix 上,可能还有任何其他合理的操作系统。
不释放内存的好处
操作系统保留分配给进程的大内存块列表,malloc 库也保留分配给 malloc 的小内存块表。
通过不释放内存,您将在进程终止时节省对这些小列表的统计工作。在某些情况下甚至建议这样做(例如 MSDN : Service Control Handler 建议 SERVICE_CONTROL_SHUTDOWN 应该通过不释放内存来处理)
不释放内存的缺点
valgrind 和应用程序验证程序等程序通过监视分配给进程的内存并报告泄漏来检查程序的正确性。
当您不释放内存时,这些将报告大量噪音,从而难以发现无意的泄漏。如果您在循环内泄漏内存,这将很重要,这将限制您的程序可以交付的任务的大小。
在我的职业生涯中,我曾多次将进程转换为共享进程 object/dll。这些是有问题的转换,因为预期由 OS 进程终止处理的泄漏开始在 "main".
的生命周期之后继续存在。