动态内存分配和内存块元数据

时间:2013-06-03 16:59:06

标签: c++ c linux windows unix

我对动态内存分配的低级问题有疑问。 我知道可能有不同的实现,但我需要了解基本的想法。

所以,

当现代OS内存分配器或等效内存分配内存块时,需要释放该块。

但是,在此之前,需要存在一些系统来控制分配过程。

我需要知道:

  • 此系统如何跟踪已分配和未分配的内存。我的意思是,系统需要知道已经分配了哪些块以及它们在分配和释放过程中使用这些信息的大小。

现代硬件是否支持此过程,例如分配位或类似的东西? 或者是用于存储分配信息的某种数据结构。 如果有数据结构,它使用了多少内存与分配的内存相比?

以大块而不是小块分配内存是否更好?为什么?

任何有助于揭示基本实施细节的答案都表示赞赏。

如果需要代码示例,C或C ++就可以了。

4 个答案:

答案 0 :(得分:2)

“此系统如何跟踪已分配和未分配的内存。”对于具有操作系统的非嵌入式系统,操作系统负责组织的virtual page table(当然具有硬件TLB支持)跟踪程序的内存使用情况。

远远超过我知道(如果我弄错的话社区肯定会对我大喊大叫)跟踪个别malloc()尺寸和位置有一个大量的实现,并且依赖于运行时库。一般来说,无论何时拨打malloc(),大小和位置都会存储在表格中。无论何时调用free(),都会查找提供的指针的表条目。如果找到,则删除该条目。如果未找到,则忽略free()(这也表示可能存在内存泄漏)。

当释放虚拟页面中的所有malloc()条目时,该虚拟页面将被释放回操作系统(这也意味着free()并不总是将内存释放回操作系统,因为虚拟页面中可能还有其他malloc()个条目)。如果给定虚拟页面中没有足够的空间来支持指定大小的另一个malloc(),则从操作系统请求另一个虚拟页面。

嵌入式处理器通常没有操作系统,虚拟页表,也没有多个进程。在这种情况下,不使用虚拟内存。相反,嵌入式处理器的整个内存被视为一个大的虚拟页面(尽管地址实际上是物理地址),内存管理遵循与前面描述的类似的过程。

Here是一个类似的堆栈溢出问题,有更深入的答案。

“以大块而不是小块分配内存会更好吗?为什么?”根据需要分配尽可能多的内存,不多也不少。编译器优化非常智能,并且几乎总是可以比程序员手动执行更有效地管理内存(即减少内存碎片)。在非嵌入式环境中尤其如此。

Here是一个类似的堆栈溢出问题,有更深入的答案(注意它与C而不是C ++有关,但它仍与此讨论相关)。

答案 1 :(得分:1)

嗯,实现这一目标的方法不止一种。 我曾经为教育目的编写了malloc()(和free())实现。

根据我的经验,现实世界的实施肯定会有所不同。

我使用了双链表。 调用malloc()后返回给用户的内存块实际上是包含我的实现的相关信息的struct(即nextprev指针,以及is_used字节)。

因此,当用户请求N个字节时,我分配了N + sizeof(my_struct)字节,隐藏了nextprev指针,并将剩余的内容返回给用户。

当然,对于使用大量小分配的程序来说,这是一个糟糕的设计(因为每个分配需要N + 2个指针+ 1个字节)。

对于真实世界的实现,您可以查看好的和众所周知的内存分配器的代码。

答案 2 :(得分:1)

通常存在两个不同的层。

一层生活在应用程序级别,通常作为C标准库的一部分。这就是您通过mallocfree(或C ++中的operator new等函数调用的内容,而这些函数通常会调用malloc)。该层负责您的分配,但不了解内存或来自何处。

在操作系统级别,另一层不知道也不关心您的分配。它只维护一个已经保留,分配和访问的固定大小的内存页面列表,以及每个页面信息,例如它映射到的位置。

任何一层都有许多不同的实现方式,但通常它的工作原理如下:
当您分配内存时,分配器(“应用程序级别部分”)会查看它是否在其书籍中的某个位置具有匹配块,它可以提供给您(如果需要,某些分配器会将更大的块拆分为两个)。 p>

如果找不到合适的块,它会从操作系统中保留一个新块(通常 大于您要求的块)。 Linux上的sbrkmmap或Windows上的VirtualAlloc将是它可能用于此效果的典型函数示例。
除了向操作系统显示 intent 并生成一些页表条目之外,这几乎没有什么作用 然后,分配器(逻辑上,在其书中)根据其正常操作模式将该大区域分成更小的块,找到合适的块,并将其返回给您。请注意,此返回的内存甚至不一定存在为phsyical内存(尽管大多数分配器将一些元数据写入每个已分配单元的前几个字节中,因此它们必然会预先对页面进行故障排除)。

同时,无形中,后台任务将某些进程使用但已被释放的内存页面清零。这种情况一直发生在一个暂定的基础上,因为迟早会有人要求记忆(通常,这就是空闲任务的作用)。

首次访问包含已分配块的页面中的地址时,会生成错误。这个尚未存在的页面的页表条目(逻辑上存在,只是不是逻辑上的)被替换为对零页池中的页面的引用。在不常见的情况下,例如,如果一直在分配大量内存,操作系统会交换出一个它认为不会很快被访问的页面,将其归零并返回此页面。登记/> 现在,该页面成为您工作集的一部分,它对应于实际的音乐记忆,并且它会考虑您的流程的配额。当您的进程正在运行时,页面可能会移入和移出您的工作集,或者可能会在您超出某些限制时,以及根据需要多少内存以及如何访问它来进行分页和分页。

调用free后,分配器会将释放的区域放回其书中。 可能告诉操作系统它不再需要内存,但通常这不会发生,因为它不是真正必要的,保持一点额外的内存并重用它更有效。此外,释放内存可能并不容易,因为通常您分配/解除分配的单元不直接与操作系统使用的单元相对应(并且,在sbrk的情况下,它们需要在也是正确的顺序。)

当进程结束时,操作系统只会抛弃所有页表条目,并将所有页面添加到空闲任务将清零的页面列表中。因此物理内存可用于下一个过程,需要一些。

答案 3 :(得分:1)

Doug Lee的malloc的源代码可以在

找到
    ftp://gee.cs.oswego.edu/pub/misc/malloc.c

它有很多关于它是如何工作的好评。