【问题标题】:How malloc works? [duplicate]malloc 是如何工作的? [复制]
【发布时间】:2011-04-26 15:12:47
【问题描述】:

可能重复:
How do free and malloc work in C?

考虑一个场景,我必须通过 malloc 分配大约 20 字节的内存。为了成功调用 malloc() 函数,这 20 个字节应该在内存中连续可用还是可以分散使用?例如,在上述情况下,如果有 4 或 5 个 10 字节的块,malloc 会起作用吗?还是这个操作系统特定或编译器特定?

【问题讨论】:

    标签: c malloc


    【解决方案1】:

    这个问题有点不对。

    在典型的操作系统中,存在虚拟内存和物理内存的概念。

    物理内存通常存在于 4kb 块中,虚拟内存也是如此。

    每个进程都有虚拟内存 - 操作系统为每个进程呈现似乎是完全可寻址的内存范围。所以在 32 位机器上,每个进程“认为”它有 4 GB 的连续内存。

    实际上,操作系统在幕后忙于将虚拟内存分配映射到真实的物理内存块。因此,比如说,400kb 的虚拟内存分配被映射到 100 个物理块上。这些物理块不需要是连续的(几乎从来没有——没有什么能阻止它的发生,但在一台做任何工作的机器上,这是极不可能的)但虚拟内存分配确实需要是连续的。

    所以你仍然会遇到虚拟内存碎片。在这里,一个进程请求一块内存,但在该特定进程的虚拟内存映射中,没有一块连续的虚拟内存可以满足该请求。

    那个问题就是你想的问题。

    【讨论】:

    • 谦卑的弓 :-) [最小帖子长度的额外填充]
    • 这不会影响从 malloc 返回的指针。
    • drwek,你是什么意思? malloc 返回虚拟内存指针 - 如果没有连续的虚拟内存地址块可以满足请求,它将影响 malloc,因为 malloc 必须返回 NULL!
    • 我无法理解您解释的最后一部分。发生虚拟内存碎片时究竟会发生什么?
    • 想象你有一个流程。它一直在进行大量的分配和释放。它的虚拟内存映射是高度碎片化的——大量的分配,大量的间隙。你可能会发现你到达了一个点,最大的差距就是 5 MB 长。然后您尝试 malloc 10 MB。这将失败。虚拟内存映射中没有连续的 10 MB 分配 - 即使有足够的物理内存可用于该分配。
    【解决方案2】:

    malloc 的调用要么成功地从程序的HEAP 内存空间返回一个逻辑上连续的内存块,该内存块等于请求的大小,要么失败并返回NULL 指针。 “逻辑上连续”意味着使用这种类型的 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 */
    

    将在您的操作系统上的 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 返回的内存块在逻辑上与您的程序不连续,您将无法使用指针算术或数组下标访问该块。

    在幕后,您的操作系统可能会移动其他可移动的内存块、使用虚拟内存、将其他项目交换到虚拟内存等,以增加分配给您的程序的 HEAP。 Malloc 可能是一个非常快速或非常昂贵的调用——取决于该系统上发生的其他事情以及分配给您的程序的 HEAP 空间。

    HEAP 内存(使用 C 中的动态系统调用访问的部分)可能会产生碎片。假设您分配了内存变得稀缺的 20 字节块的数量。现在想象你释放这些块中的每个其他块。您将拥有高度碎片化的内存,因为使用malloc 分配的块如果影响程序用于访问块的指针,则无法移动。 (它可以透明地移动,但不要指望它是有效的。)

    如果您多次调用 HEAP 内存,请考虑更改您的逻辑以使用 realloc 根据需要增加和缩小内存。 realloc 的一个大“陷阱”是指向现有数据的指针可能会更改,因此仅使用 1 个指向它的指针。 Realloc 允许操作系统根据需要移动数据,以更好地适应 HEAP 上的可用数据。这样,您将(大部分)避免内存碎片的可能性。

    对于 20 字节的快速块,请考虑使用 STACK。这就是它的用途。查看 this SO post 以了解 STACK 与 HEAP 的特征。

    阅读C Guide of calloc, malloc, realloc, free 了解更多信息。

    【讨论】:

    • realloc 如果无法直接展开块,会将第一块的内容复制到第二块。如果您不想让它复制,请不要调用realloc 作为优化。
    【解决方案3】:

    标准 malloc 在 C 标准中定义为分配一个连续的内存块(至少在您看来是这样) - 如果分配失败,它将返回一个空指针。

    在较低级别,操作系统将执行 kotlinskiBlank Xavier 在各自答案中描述的操作。

    来自ISO/IEC 9899-1999 C 标准§7.20.3

    如果分配(callocreallocmalloc)成功,则返回的指针经过适当对齐,以便它可以分配给指向任何类型对象的指针,然后 用于访问已分配空间中的此类对象或此类对象数组(直到显式释放空间)。

    不是那么明确,但该段落提到“访问此类对象的数组”,在 C 标准中,数组是:

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

    另请注意,对callocreallocmalloc 的后续调用不保证内存的连续性或顺序(其他内存块已分配)。

    §7.20.3 中也规定了这一点。

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

    【讨论】:

    • realloc 的后续调用将返回一个指向该大小的连续内存的指针失败。如果之前正确分配,它还将保护您在该区域中的现有数据。指针可以改变,数据的绝对地址也可以改变,但如果realloc 成功,则您的原始数据和调整大小的块为一个块。关于realloc...,您最后的陈述是错误的
    • 我最后的陈述可能不清楚 - 我的意思是重复调用任何分配函数并不能保证与已分配的其他内存块相关的连续性/顺序,而不是分配的内存他们不会是连续的。
    【解决方案4】:

    这是特定于平台的。在您看来,在软件级别上,它总是会在您 malloc:ed 20 个连续字节时呈现。但在某些平台上,例如使用分页内存,这些字节在实际硬件中并不需要是连续的 - 它只是看起来如此。

    【讨论】:

      【解决方案5】:

      内存必须是连续的 - 你所说的通常被称为memory fragmentation,对于经常获取和释放内存的应用程序来说是一个真正的问题。

      【讨论】:

        【解决方案6】:

        特定于操作系统。

        如果它是一个具有虚拟内存的系统,malloc 只会分配 VM 的另一页并将其添加到它的堆中,然后将你需要的内存交给你。但是,页面内的内存块必须是连续的。有趣的是,如果您的需求大于页面大小,则内存不必在物理内存中是连续的。无论它们位于物理内存中的什么位置,都可以使 VM 的两个页面连续。

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

        【讨论】:

          【解决方案7】:

          来自 ISO C99 标准:

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

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

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

          【讨论】:

            【解决方案8】:

            很可能它不会工作。 malloc 无法重新排列已分配的内存,因此它无法生成 20 字节的连续空闲块。它只能要求操作系统从中获取另一页内存,它可以砍掉20字节+头。

            【讨论】:

            • 抱歉,这有很多问题。 "malloc" can realloc 内存,并且在各种情况下它可以为您返回相同的位置。那是因为 malloc 实现通常会给你比你要求的更多的内存(例如,一个流行的方案是将内存分成桶,每个桶包含一定大小的内存区域;然后 malloc 会给你一个桶中可以容纳的元素至少需要的大小)。此外,只有当桶为空时,malloc 才会要求操作系统为小尺寸提供新页面。不过,它通常会在分配大尺寸时询问。
            • @DarkDust:我同意答案还没有得到很好的解释,但你对它的解构也有一些不足之处。我不能将“malloc 可以重新分配内存,并且在各种情况下它可以为您返回相同的位置”解释为准确描述 malloc 行为的有意义的陈述。
            猜你喜欢
            • 2021-05-18
            • 2012-06-22
            • 1970-01-01
            • 2016-01-19
            • 2014-08-25
            • 2011-10-02
            • 2011-10-22
            • 2011-04-15
            • 2015-09-24
            相关资源
            最近更新 更多