堆内存分配

时间:2015-08-22 08:13:40

标签: c memory malloc dynamic-memory-allocation

如果我使用malloc()在我的程序中动态分配内存但是在程序运行期间我没有释放内存,那么在程序终止后是否会释放动态分配的内存?

或者如果它没有被释放,并且我一遍又一遍地执行相同的程序,它每次都会分配不同的内存块吗?如果是这样的话,我应该如何释放这段记忆?

注意:我能想到的一个答案是重新启动我正在执行程序的机器。但是,如果我在远程计算机上执行程序并且重启不是一个选项?

5 个答案:

答案 0 :(得分:8)

当程序终止时,应该释放分配的程序的任何内存,无论它是静态分配还是动态分配。对此的主要例外是如果进程分叉到另一个进程。

如果您没有明确free任何内存malloc,它将保持分配状态,直到该过程终止。

答案 1 :(得分:3)

即使您的操作系统在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
}

请注意,使用main的返回值调用exit。在没有动态链接器或线程的静态构建中,exit()可以是直接内联的syscall(__NR_exit,main(...));但是如果你的libc使用exit()的包装器来执行*_fini()例程(大多数libc实现都这样做),那么在main()终止后仍然有一个函数需要调用。

恶意程序可以LD_PRELOAD exit()或它调用的任何例程,并将其转换为一种永远不会释放其内存的僵尸进程。

即使您在free()之前执行exit(),该进程仍将消耗一些内存(基本上是可执行文件的大小,在某种程度上是其他进程未使用的共享库) ,但是某些操作系统可以重复使用非malloc()内存,以便后续加载同一个程序,这样你就可以在没有注意到僵尸的情况下运行数月。

FWIW,大多数libc实现都有某种exit()包装器,除了dietlibc(当构建为静态库时)和我只部署的静态libc.h,我只发布在Puppy Linux论坛。

答案 2 :(得分:1)

正如我们所说,操作系统的大脑是内核。操作系统有几个职责。

内存管理是内核的功能。

  

内核可以完全访问系统的内存,并且必须允许进程   根据需要安全地访问这个内存。

这样做的第一步通常是虚拟寻址,通常通过分页和/或分段来实现。虚拟寻址允许内核使给定的物理地址看起来是另一个地址,即虚拟地址。对于不同的进程,虚拟地址空间可能不同;一个进程在特定(虚拟)地址访问的内存可能与另一个进程在同一地址访问的内存不同。

  

这允许每个程序的行为就像它是唯一的一样(除此之外)   从内核运行,从而防止应用程序崩溃   彼此

Memory Allocation

<强>的malloc

  

分配内存块

.NET等效:不适用。要调用标准C函数,请使用 PInvoke

<小时/> The Heap

  

堆是计算机内存中未管理的区域   自动为您服务,而不是由CPU严格管理。它是   一个更自由浮动的内存区域(并且更大)。分配   堆上的内存,你必须使用malloc()calloc()   内置C函数。一旦你在堆上分配了内存,你就可以了   负责使用free()一旦你释放那个记忆   不再需要了。如果你没有这样做,你的程序就会有   所谓的内存泄漏。也就是说,堆上的内存会   仍然被搁置(并且不会被其他进程使用)。

<小时/> 内存泄漏

For Windows

当进程从分页或非分页池分配内存但未释放内存时,会发生内存泄漏。结果,这些有限的内存池随着时间的推移而耗尽,导致Windows速度变慢。如果内存完全耗尽,可能会导致故障。

Preventing Memory Leaks in Windows Applications

内存泄漏是一类错误,应用程序在不再需要时无法释放内存。随着时间的推移,内存泄漏会影响特定应用程序和操作系统的性能。由于过度分页,大量泄漏可能导致不可接受的响应时间。最终,应用程序以及操作系统的其他部分都将出现故障。

  

Windows将释放应用程序在进程中分配的所有内存   终止,因此短期运行的应用程序不会影响整体   系统性能显着。但是,长时间运行中的泄漏   像服务甚至是Explorer插件这样的流程都会产生很大的影响   系统可靠性可能会强制用户按顺序重启Windows   使系统再次可用。

应用程序可以通过多种方式代表他们分配内存。如果在使用后未释放,则每种类型的分配都可能导致泄漏

。以下是常见分配模式的一些示例:

  • 通过HeapAlloc函数或其C / C ++运行时堆内存 等效mallocnew

  • 通过VirtualAlloc从操作系统直接分配 功能

  • 通过Kernel32 API(例如CreateFile)创建的内核句柄, CreateEventCreateThread代表内核保留内核内存 应用

  • 通过User32和Gdi32 API创建的GDI和USER句柄(默认情况下, 每个进程都有10,000个句柄的配额)

For Linux

memprof 是一种用于分析内存使用情况和查找内存泄漏的工具。        它可以生成一个配置文件,每个内存分配了多少内存        在你的程序中运行。此外,它可以扫描内存并查找块        您已分配但不再在任何地方引用。

答案 3 :(得分:1)

  

如果我使用malloc()在程序中动态分配内存但是我   在程序运行期间不释放内存,将动态   程序终止后释放分配的内存?

操作系统将释放通过malloc分配的内存,以供其他系统使用。

这比你的问题听起来要复杂得多,因为进程使用的物理内存可能被写入磁盘(分页)。但是对于Windows,Unix(Linux,MAC OS X,iOS,android),系统将释放它为此过程提供的资源。

  

或者如果它没有被释放,我会一遍又一遍地执行相同的程序   再次,它会每次分配不同的内存块吗?如果   就是这样,我应该如何释放这段记忆呢?

每次启动程序,都会获得一组新内存。这取自系统,并作为虚拟地址提供。现代操作系统使用地址空间布局随机化(ASLR)作为安全功能,这意味着堆应该在每次程序启动时提供唯一的地址。但是,由于其他运行的资源已被整理,因此无需释放内存。

正如您所指出的,如果后续运行无法跟踪其已提交资源的位置,那么它们如何能够释放它们。

另请注意,您可以在同一时间运行程序多次启动。分配的内存可能看起来重叠 - 每个程序可能看到分配的相同地址,但这是“虚拟内存” - 操作系统已独立设置每个进程,因此它似乎使用相同的内存,但与每个进程关联的RAM将是独立的。

执行程序时,不释放程序的内存将在Windows和Unix上运行,并且可能是任何其他合理的操作系统。

不释放内存的好处

操作系统保留一个分配给进程的大内存块列表,并且malloc库保留分配给malloc的小块内存表。

通过不释放内存,您将在进程终止时保存这些小列表的工作记帐。在某些情况下甚至建议这样做(例如MSDN : Service Control Handler表示应该通过NOT释放内存来处理SERVICE_CONTROL_SHUTDOWN)

不释放内存的缺点

诸如valgrind和应用程序验证程序之类的程序通过监视分配给进程的内存并报告泄漏来检查程序的正确性。

当你不释放记忆时,这些会报告很多噪音,很难发现无意的泄漏。如果你在循环中泄漏内存,这将是非常重要的,这将限制程序可以提供的任务的大小。

在我的职业生涯中,我已经多次将进程转换为共享对象/ dll。这些都是有问题的转换,因为预期由操作系统进程终止处理的泄漏,开始存活超过“主要”的生命。

答案 4 :(得分:0)

malloc分配的内存需要由分配程序释放。如果没有并且内存保持分配,那么程序将耗尽允许的内存分配并抛出分段或内存不足错误。 malloc的每一组内存分配都需要免费提供。