malloc如何工作?

14

可能是重复问题:
在C语言中,free和malloc是如何工作的?

考虑这样一种情况:我需要通过malloc分配20个字节大小的内存。为了使malloc()函数调用成功,这20个字节必须在内存中连续吗?还是可以分散在多个位置?例如,在上述情况下,如果有4或5个10字节的块,malloc会起作用吗?或者这取决于操作系统或编译器?

8个回答

48
在典型的操作系统中,存在虚拟内存和物理内存的概念。
物理内存通常以4kb块的形式存在,虚拟内存也是如此。
每个进程都有虚拟内存 - 对于每个进程,操作系统呈现出似乎是完全可寻址的内存范围。因此,在32位机器上,每个进程“认为”它有4 GB的连续内存。
实际上,在幕后,操作系统正在将虚拟内存分配映射到实际的物理内存块上。因此,例如,400kb的虚拟内存分配被映射到100个物理块上。这些物理块不需要是连续的(几乎从不是 - 没有什么可以阻止它发生,但在进行任何工作的机器上,这是高度不可能的),但虚拟内存分配需要是连续的。
因此,您仍然可能遇到虚拟内存碎片。在这里,进程请求一块内存,但在该特定进程的虚拟内存映射中,没有一块连续的虚拟内存块,使得请求可以满足。
那个问题就是你所考虑的问题。

1
谦虚的鞠躬 :-) - user82238
1
这不会影响从 malloc 返回的指针。 - dawg
drwek,你是什么意思?malloc返回虚拟内存指针——如果没有一块连续的虚拟内存地址可以满足请求,那么它将影响malloc,因为malloc将不得不返回NULL! - user82238
1
我无法理解你解释的最后一部分。当虚拟内存碎片化发生时,会发生什么? - Raj
4
想象一下你有一个程序,它进行了大量的分配和释放操作。它的虚拟内存映射非常分散——有很多分配,也有很多空隙。当最大的空隙不足5兆字节时,您可能会发现您试图malloc 10兆字节时将失败。即使有足够的物理内存可供分配,但在虚拟内存映射中并没有连续的10兆字节的空间。 - user82238

9
调用 malloc 函数将会成功返回一个大小等于请求的内存块,该内存块在程序的堆内存空间中是逻辑连续的,否则将返回空指针。"逻辑连续"意味着使用这种类型的 malloc
int *ip;      /* Nothing yet allocated (other than the size of a pointer... */
int ar[100];  /* 100 ints on the STACK */
ip = (int *)malloc(sizeof ar);   /* if that succeeds, 100 ints on the HEAP */

该函数将在您的操作系统的堆上为100个整数分配空间,并返回NULL或指针。另外,数组ar是在栈上分配的。每个数组都将被逻辑地放置在所有整数旁边,至少在程序知道的范围内如此。如果它们不相邻,则无法使用array[offset]符号或指针算术来将这些块作为数组寻址。

然后,您可以通过数组访问或指针访问交替访问堆栈或堆块的内存,就像这样:

ip[2]=22;        /* the second element of ip[] is '22' */
*(ar+33)=3333;   /* the 33 element of ar is '3333' */

i=*(ip+2);       /* assign using pointers */
j=ar[33];        /* assign using array offsets */

如果malloc返回的内存块与您的程序不是逻辑上连续的,您将无法使用指针算术或数组下标访问该块。
在幕后,您的操作系统可能会移动其他可移动的内存块,使用虚拟内存,将其他项目交换到虚拟内存中等等,以增加分配给您的程序的堆空间。根据系统上正在发生什么以及分配给您的程序的堆空间,malloc可以是非常快速或非常昂贵的调用。
HEAP内存(使用C中的动态系统调用访问的部分)可能会受到碎片化的影响。假设您分配了20个字节块的数量,其中内存变得稀缺。现在想象一下,您释放了这些块中的每隔一个块。由于使用malloc分配的块不能移动,如果它影响程序用于访问该块的指针,则您将具有高度碎片化的内存。(它可以被透明地移动,但不要指望那是有效的。)
如果您正在进行许多HEAP内存调用,请考虑更改逻辑以使用realloc根据需要增加和缩小内存。realloc的一个大问题是指向现有数据的指针可能会更改,因此只使用1个指针。realloc允许操作系统根据HEAP上可用的内容更好地移动数据。这样,您将(大多数情况下)避免了内存碎片化的可能性。
对于20字节的快速块,请考虑使用STACK。那就是它的作用。请查看这篇SO帖子以查看STACK与HEAP的特征。
阅读calloc、malloc、realloc、free的C指南以获取更多信息。

1
如果realloc无法直接扩展块,则会将内容从第一个块复制到第二个块。如果您不希望进行复制,请勿调用realloc以进行优化。 - Johnny V

8

malloc是C标准中定义的用于分配连续内存块的函数(至少对用户来说是这样),如果分配失败则返回空指针。

更底层地讲,操作系统会执行像kotlinskiBlank Xavier在各自的回答中所描述的操作。

以下内容摘自ISO/IEC 9899-1999 C标准§7.20.3:

如果通过calloc、reallocmalloc分配成功,则返回的指针已经适当地对齐,以便将其指定为任何类型对象的指针,然后使用该空间分配的对象或对象数组的访问(直到显式释放该空间为止)。

虽然该段落没有明确说明,但它提到了“访问对象数组”,而在C标准中,数组是:

数组类型描述了一组具有特定成员对象类型(称为元素类型)的连续分配非空对象。(来自于§6.2.5)

此外,连续调用callocreallocmalloc不保证内存的相对位置或顺序(与已分配的其他内存块相比)。

这一点也在§7.20.3中说明:

通过连续调用 callocmallocrealloc 函数所分配的存储空间的顺序和连续性未指定。


realloc 的后续调用将会返回指向该大小的连续内存块的指针,或者失败。如果在正确分配之前进行了分配,则它还将保护该区域中的现有数据。指针可能会改变,数据的绝对地址也可能会改变,但是如果 realloc 成功,您将拥有原始数据和调整大小的块作为一个块。关于 realloc,您最后的陈述是错误的... - dawg
1
我的上一个陈述可能不太清楚 - 我的意思是,对任何分配函数的重复调用并不能保证它们分配的内存块与已经分配的其他内存块相邻/有序,而不是它们分配的内存本身不是连续的。 - wkl

5

这是平台特定的。在软件层面上,对于你的眼睛来说,它总是呈现为你malloc了20个连续的字节。但在某些平台上,例如具有分页内存的平台,当涉及到实际硬件时,这些字节实际上并不需要是连续的-它只是看起来是这样的。


2

内存必须是连续的 - 您提到的通常被称为内存碎片化,对于频繁获取和释放内存的应用程序来说,这是一个真正的问题。


2

针对不同的操作系统。

如果这是一个具有虚拟内存的系统,malloc将会分配另一页虚拟内存并将其添加到堆中,然后将所需的内存交给您。但是,页面内的内存块必须是连续的。有趣的是,如果您的需求大于页面大小,则物理内存中的内存块不必是连续的。两个虚拟内存页面可以被连接起来,而无论它们在物理内存中的位置如何。

如果这是一个没有虚拟内存的系统,那么是的,您请求的所有内存都需要在物理内存中是连续的。


1
ISO C99标准:

7.20.3内存管理函数
calloc、malloc和realloc函数分别调用时分配的存储空间的顺序和连续性是未指定的。如果分配成功,则返回的指针适当对齐,以便可以将其分配给任何类型的对象指针,然后用于访问所分配的空间中的此类对象或此类对象的数组(直到明确释放该空间)。分配的对象的生存期从分配到释放。每个这样的分配都应产生指向与任何其他对象不相交的对象的指针。返回的指针指向分配空间的开始(最低字节地址)。如果无法分配空间,则返回空指针。如果请求的空间大小为零,则行为是实现定义的:要么返回空指针,要么行为就像大小为一些非零值,但返回的指针不得用于访问对象。

7.20.3.3 malloc函数
malloc函数为大小由size指定且值不确定的对象分配空间。

换句话说,如果您请求一个20字节对象的空间,您将获得足够容纳20字节对象的空间。

-1

很可能它不会起作用。malloc无法重新排列已分配的内存,因此无法创建一个连续的20字节空闲块。它只能向操作系统请求另一页内存,然后从中切割出20个字节+头。


抱歉,但这里有很多问题。 "malloc" 可以 realloc 内存,在各种情况下它可以给你完全相同的位置。这是因为通常 malloc 实现会给你比你请求的内存更多的内存(例如,一种流行的方案是将内存分成桶,每个桶包含一定大小的内存区域;然后 malloc 将从一个可以容纳所需大小的桶中给你一个元素)。此外,malloc 不会为微小的大小请求新页面,只有在桶为空时才会这样做。不过,当分配大块内存时,它通常会请求。 - DarkDust
1
@DarkDust:我同意答案还没有得到很好的解释,但是你对它的分解也有些欠缺。我不能将“malloc可以重新分配内存,在各种情况下它可以给你相同的位置”解释为一个准确描述malloc行为的有意义的语句。 - Jonathan Leffler

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