在多次分配/释放小块内存后无法成功分配大块内存

5

以下是代码。

首先,我尝试分配并释放一个大块内存,然后我分配许多小块内存,直到内存用尽,并释放所有这些小块。

之后,我尝试分配一个大块内存。

#include <stdio.h>
#include <stdlib.h>
int main (int argc, char **argv)
{
    static const int K = 1024;
    static const int M = 1024 * K;
    static const int G = 1024 * M;

    static const int BIG_MALLOC_SIZE = 1 * G;
    static const int SMALL_MALLOC_SIZE = 3 * K;
    static const int SMALL_MALLOC_TIMES = 1 * M;

    void **small_malloc = (void **)malloc(SMALL_MALLOC_TIMES * sizeof(void *));

    void *big_malloc = malloc(BIG_MALLOC_SIZE);
    printf("big malloc first time %s\n", (big_malloc == NULL)? "failed" : "succeeded");
    free(big_malloc);

    for (int i = 0; i != SMALL_MALLOC_TIMES; ++i)
    {
        small_malloc[i] = malloc(SMALL_MALLOC_SIZE);
        if (small_malloc[i] == NULL)
        {
            printf("small malloc failed at %d\n", i);
            break;
        }
    }
    for (int i = 0; i != SMALL_MALLOC_TIMES && small_malloc[i] != NULL; ++i)
    {
        free(small_malloc[i]);
    }

    big_malloc = malloc(BIG_MALLOC_SIZE);
    printf("big malloc second time %s\n", (big_malloc == NULL)? "failed" : "succeeded");
    free(big_malloc);

    return 0;
}

以下是结果:

big malloc first time succeeded
small malloc failed at 684912
big malloc second time failed

看起来似乎存在内存碎片。

我知道当内存中有许多小的空闲空间,但没有足够大的空闲空间来进行大块的malloc时,就会发生内存碎片。

但是我已经释放了所有我分配的内存,内存应该是空的。

为什么第二次我无法进行大块的malloc?

我在Windows 7上使用Visual Studio 2010构建32位程序。


2
也许内存仍然是碎片化的。您尝试过在程序运行时监视内存发生了什么吗? - Ashalynd
2
@DieterLücking 我也这么认为,但 free() 循环会在遇到第一个 NULL 时终止。 - unwind
3
在smalloc malloc失败时,malloc会返回NULL。在第二个for循环中,我检查small_malloc[i] != NULL来保护未初始化的指针。 - Celebi
2
显然,VC++ 无法重新组合相邻的空闲内存块。在 Linux 32 位上使用 GCC,它可以正常工作。 - glglgl
1
问题不在于VC++,而在于libc的堆管理器。默认情况下,Windows的堆管理器非常保守,但有一个API可以请求使用新的堆管理器。 - toasted_flakes
显示剩余5条评论
3个回答

4
很遗憾,答案仍然是分散的。
你最初的大内存分配最终由一个分配块跟踪;但是当你开始分配大量3k内存块时,你的堆被切成了块。
即使你释放了内存,块中的小片段仍会留在进程的地址空间中。您可以使用类似Sysinternals VMMap这样的工具来直观地查看这些分配。
看起来分配器使用了16M块,一旦这些块被释放,它们就永远不会返回到空闲池中(即这些块仍然分配)。
因此,第二次你没有足够连续的内存来分配1GB块。

0

1
只是一条评论:我在Windows 7 32位/4GB内置CVI 2013下尝试了您的代码,并没有遇到您描述的问题,因此很可能是VC2010(或其子部分)特定的问题。 - Jaroslav

0
问题可能在于,即使您释放了每个分配,malloc也不会将所有内存返回给操作系统。
当您的程序请求许多较小的分配时,malloc必须增加其分配内存的“arena”的大小。
如果您释放了所有内存,不能保证arena会缩小到原始大小。可能仍然存在arena,并且所有块已被放入空闲列表中(可能合并为较大的块)。
在您的地址空间中存在这个残留的arena可能会使满足大型分配请求变得不可能。

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