8

考虑以下 C 代码,它创建 100,000 个 4KB 大小的页面,然后释放 99,999 个页面,最后释放最后一个页面:

#include <stdio.h>
#include <stdlib.h>

#define NUM_PAGES 100000

int main() {
    void *pages[NUM_PAGES];

    int i;
    for(i=0; i<NUM_PAGES; i++) {
        pages[i] = malloc(4096);
    }

    printf("%d pages allocated.\n", NUM_PAGES);
    getchar();

    for(i=0; i<NUM_PAGES-1; i++) {
        free(pages[i]);
    }

    printf("%d pages freed.\n", NUM_PAGES-1);
    getchar();

    free(pages[NUM_PAGES-1]);

    printf("Last page freed.\n");
    getchar();

    return 0;
}

如果你编译它,运行它并监控进程的内存使用情况,你可以看到内存使用量在第一次之前达到了大约 400MB getchar(当内存分配给 100,000 页时),然后即使在 99,999 页被 de-分配(在第二个之后getchar),最后,当最后一页被取消分配时,它下降到 1MB。

所以,我的问题是为什么会这样?为什么只有在所有页面都被释放时才将整个内存返回给操作系统?是否有任何页面大小或任何页面对齐方式可以防止这种事情发生?我的意思是,当只有一页被释放时,是否有任何页面大小或对齐方式使任何分配的页面完全返回给操作系统?

4

1 回答 1

5

这完全取决于实现,但我相信这与内存分配器的工作方式有关。通常,当内存管理器需要来自操作系统的更多内存时,它会调用该sbrk函数来请求额外的内存。该函数的典型实现是操作系统在内存中存储一​​个指针,指向进程可以获得空间的下一个空闲地址。内存像堆栈一样增长,与调用堆栈的工作方式非常相似。例如,如果您分配了五页内存,它可能如下所示:

 (existing memory) | Page 0 | Page 1 | Page 2 | Page 3 | Page 4 | (next free spot)

使用此设置,如果您释放第 0 - 4 页,程序内部的内存管理器会将它们标记为空闲,如下所示:

 (existing memory) |                                   | Page 4 | (next free spot)

由于操作系统以类似堆栈的方式分配内存,因此在第 4 页完成使用之前,它无法从程序中回收所有这些内存。释放最后一页后,进程的内存将如下所示:

 (existing memory) |                                              (next free spot)

此时程序的内存管理器可以将大量可用空间返回给操作系统:

 (existing memory) | (next free spot)

换句话说,因为内存是作为堆栈分配的,所以在您释放分配的最后一个东西之前,操作系统无法回收任何内存。

希望这可以帮助!

于 2013-05-28T00:11:11.677 回答