malloc和堆:额外存储大小和链表信息的内存?

4

我有一个关于 heapmalloc 的简单问题:

当我们使用 malloc 分配一些内存空间,如下所示:

int *p;
p = (int*) malloc (10*sizeof(int));

实际上,在堆中分配了10个单词的空间。然而,我的问题是:

实际使用的内存空间是否真的是10个单词?

还是需要其他额外的空间来存储内存大小的值?

甚至,由于堆被构造为链表,是否还有其他的内存空间用于存储指向堆中下一个节点的地址?


6
是的,malloc有开销。这完全取决于具体的实现方式。但通常情况下,如果程序使用较少的大型分配而不是许多小型分配,则程序将更加高效地利用内存空间。 - Lee Daniel Crocker
1
这是实现定义的。然而,在任何情况下,都需要一些开销来管理已分配的内存。您可以从以下链接开始阅读:https://en.wikipedia.org/?title=Memory_management - user2249683
可能是如何使用malloc()和free()?的重复问题。 - StilesCrisis
虽然问题基本相同,但那里的答案比这里少了一些阐述。 - wallyk
4个回答

7

这完全取决于实现。

a) 每个已分配节点之前可能有几个字节,其中包含节点的大小、指向下一个节点的指针,以及可能是前一个节点指针和节点类型。

b) 返回的项可能除了其他分配之外没有任何东西。另一个结构在别处跟踪已分配和空闲的内容,可能通过位图或小型并行列表来完成。

c) 另一种变体提供几个固定大小块的数组。一个数组可以提供32字节块;另一个是128字节块等等。每个数组的位图管理分配。

d) 我见过的最基本的实现完全忽略了 free()(也就是说,free() 是一个无操作),并在每个 malloc() 上分配池的下一个部分。


到目前为止,最常用的现代技术是 a。变体 b 在许多文件系统中使用,如 NTFS 和 FAT。选项 c 在许多 DEC 操作系统中特别喜爱,尤其是用于内核。选项 d 被一些极简主义嵌入式环境所使用,并带有适当的警告。

在大多数实现中,请求的分配被舍入到某个自然的倍数(通常是 2、8、16 等)方便算法。因此,一系列分配的大小为 5、3、8、7、4、1 和 15 可以被视为每个 16 字节的请求。


2
值得注意的是,还有许多其他技巧可能会占用额外的空间,例如使用特殊的位模式进行填充以检测越界写入或为调试目的提供有关分配的信息(代码的哪个部分进行了分配)。 - Karoly Horvath
也许可以加一些关于乐观分配的内容。(顺便说一句:非常好的答案!) - chux - Reinstate Monica
@chux:手册中提到的乐观分配很有趣,但我不明白它是如何适用的。我认为它意味着sbrk()是乐观的,并可能导致malloc()的乐观,对吗? - wallyk
OP说:“实际使用的内存空间只有10个字吗?”因此在考虑malloc()和其他函数是否真正“使用”了内存,当实际使用发生时,“额外的内存”可能会改变,而实际上只是标记为已使用但尚未使用。请参见这里。如果这不能帮助你,就不要费心回答了。注意:乐观分配是与sbrk()的概念是不同的。 - chux - Reinstate Monica

2

内存分配取决于编译器库和操作系统。

这两种语言都没有规定可以分配的最大内存量。您保证的仅是所请求的大小。

因此,如果有任何额外的内存分配,它将取决于平台。

另外,分配更大的空间时可能会减少开销。

尝试编写自己的内存分配器,并查看所需内容,特别是在处理内存时。


1
是的,实现malloc的代码可以分配比你请求的内存稍微多一点的空间,将已分配内存的大小存储在已分配内存的开始位置,然后给你一个指向下一个内存地址的指针。当你使用该指针调用free时,分配器会回到前面一点,读取缓冲区的大小,并计算需要释放多少空间。
当然,另一种可能的实现方法可以保留列表、字典或者做出完全不同的操作,只要它能提供相同的指定行为即可。

1
当你使用malloc分配内存时,你只会得到一个指向该内存中第一个地址的指针,并且保证已经为你分配了这么多字节供你使用。内存分配和跟踪的细节是依赖于平台的,你无法从程序内部访问这些信息。因此,虽然可能会为开销目的分配额外的内存,但你无法以跨平台的方式使用这些知识。

当然。我简化了情况,因为考虑malloc失败的情况并不适用于这个问题。 - Sam Estep

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接