堆什么时候用于动态内存分配?

堆什么时候用于动态内存分配?

每一篇关于内存分配的文章似乎要么解释如何mmap使用,要么解释如何sbrk使用,而不考虑如何将它们与堆联系起来。

我发现堆在内存分配中扮演着几乎微不足道的角色——事实上,我不确定它的作用:D,我请求有人不要让我感到困惑。


我是这样理解的:

1) 当内存被malloced时,未初始化数据段的末尾,即BSS,被扩展。这种扩展(x例如将地址移动到x-n)是由于调用 而发生的sbrk。在此模型中,内存被分配到减少 BSS 段头位置的n字节(假设每个地址对应一个字节) 。sbrk该模型现已过时。有些人将堆定义为所有此类扩展的聚合空间。其他人则不然——在后一种情况下,堆有什么作用?

2)在现代内存分配方案中,堆确实存在(出于什么原因,我也不确定)。为了分配内存,malloc内部使用mmap将数据存储到作为页面集合的内存区域中。这些内存区域独立于堆。


总括:

对于旧系统:如果内存分配存储在增加结束 BSS 的偏移量后获得的地址空间中,那么堆有什么用处吗?

对于较新的系统:假设 mmap 主要用于内存分配,那么堆有什么用途呢?

在这两种情况下,堆真的有什么用处吗?

答案1

mmapsbrk是内核提供的用于为进程分配地址空间的系统调用。这些调用更改虚拟地址到物理页框的映射。对这些映射的地址限制之外的内存进行寻址是严格禁止的,并且会导致分段错误。这是内核向进程提供的低级接口,以brk地址结尾的内存区域通常称为堆。

内核不知道有关malloc或 的任何信息free,这些是 libc 中的库函数。 Libc 维护数据结构并从内存分配的角度记录哪些内存区域是空闲的,例如 .如果可以通过重用先前释放的内存区域来满足对 的调用,则对 的调用malloc不一定会导致对sbrk或 的调用mmap(取决于 Libc 如何实现动态内存分配)来扩展映射。malloc

答案2

“堆”是一个高级的想法,而不是低级的实现。在 C 语言中,堆是malloc()用于提供分配的内存池。

这是一个有趣、简单的内存分配器实现:

static char *heap[1000000];
int top = 0;

void *malloc(int size) {
    void *ret = &heap[top];
    top += size;
    return ret;
}

void free(void *ptr) {
    /* Eh; freeing is too hard */
    return;
}

这是一个糟糕的分配器,但是对于正确类型的程序,如果您的系统和编译器对内存对齐之类的事情宽容,那么它就可以工作。它的堆是heap[]数组,它在编译时使用数组声明来构建它,而不是使用sbrk()or组装数组mmap()

“真正的”分配器以同样的方式工作。当malloc()被调用时,它会占用一小部分内存池(堆)并将其保留用于该分配。有两个重要的区别:

  • 真正的分配器知道如何free()分配,以便稍后可以再次使用它。
  • 当所有现有块都已满时,实际分配器可以通过向操作系统请求更多内存(使用sbrk()或)来增加其堆的大小。mmap()

相关内容