考虑以下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;
}
如果你编译它,运行它并监控过程&#39;在内存使用情况下,您可以看到内存使用量在第一个getchar
之前达到约400MB(当内存分配为100,000页时),即使在取消分配99,999个页面之后它也保持不变(在第二个{{之后) 1}}),最后,当最后一页被解除分配时,它会下降到1MB。
所以,我的问题是为什么会发生这种情况?为什么只有在释放所有页面时,整个内存才会返回到操作系统?是否有任何页面大小或任何页面对齐,以防止这种情况发生?我的意思是,当只有一个页面被释放时,是否有任何页面大小或对齐使得任何malloced页面完全返回到操作系统?
答案 0 :(得分:5)
这完全取决于实现,但我认为这与内存分配器的工作方式有关。通常,当内存管理器需要来自操作系统的更多内存时,它会调用sbrk
函数来请求额外的内存。该函数的典型实现是OS存储指向存储器中下一个空闲地址的指针,其中进程可以获得空间。内存像堆栈一样增长,与调用堆栈的工作方式大致相同。例如,如果您分配了五页内存,则可能如下所示:
(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)
换句话说,因为内存被分配为堆栈,所以在你释放最后分配的内容之前,操作系统无法回收任何内存。
希望这有帮助!