在Windows 7 64位上执行的C代码示例(取自http://bugs.python.org/issue19246),在32位模式下编译时
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int create_huge_linked_list(void **last_item) {
int i;
void *prev_item = NULL;
for(i = sizeof(void *); i < 1000000; i++) {
void *new_item = malloc(i);
if(new_item == NULL) {
break;
}
*(void **)new_item = prev_item;
prev_item = new_item;
}
*last_item = prev_item;
return i;
}
void free_linked_list(void *last_item) {
while(last_item != NULL) {
void *prev_item = *(void **)last_item;
free(last_item);
last_item = prev_item;
}
}
int stress_heap() {
void *last_item;
int amount = create_huge_linked_list(&last_item);
free_linked_list(last_item);
return amount;
}
void stress_twice(void) {
int first = stress_heap();
int second = stress_heap();
printf("%i %i %f%%\n", first, second, 100.0 * second / first);
}
void stress_and_alloc_1_mb() {
void *ptr;
ptr = malloc(1000000);
if(ptr != NULL) {
printf("Successfully allocated 1 MB before stress\n");
free(ptr);
stress_heap();
ptr = malloc(1000000);
if(ptr != NULL) {
printf("Successfully allocated 1 MB after stress\n");
free(ptr);
} else {
printf("Failed to allocate 1 MB after stress\n");
}
} else {
printf("Failed to allocate 1 MB before stress\n");
}
}
int main() {
stress_and_alloc_1_mb();
stress_twice();
return 0;
}
输出:
Successfully allocated 1 MB before stress
Failed to allocate 1 MB after stress
64855 64857 100.003084%
结果可能被解释为:在整个内存分配然后释放它之后,进程的内存碎片如此糟糕,没有长度为1 MB的块。 但是,压力过程可以连续重复,没有记忆错误。
问题是:
答案 0 :(得分:3)
这是一个有趣的问题,因为我们从未真正谈论过分散的使用内存(现在让我们忽略缓存局部性)。当所述内存可以再次分发时,内存碎片成为一个问题,但是先前的内存分配将内存池分成更小的块需要将块连接在一起。
我认为你问这个问题的原因是因为你已经习惯了最坏情况的例子,其中没有这样的连续内存块存在而没有移动任何东西。但即使在非最坏的情况下,检测潜在的碎片化操作也是一个非常困难的问题,导致分配器阻塞问题的实例,人类可以很容易地看到解决方案。
但要回答你的问题,请考虑这个分配的内存块:
aaaabbbbcccc
现在a
和c
被释放:
....bbbb....
现在b
被释放了:
....____....
现在我们在内存池中有三个连续的单独内存块 - 它需要特殊的代码来检测它,显然分配器会对此产生阻塞。
答案很简单:对内存池进行碎片整理。这个问题多么容易/困难取决于使用什么类型的分配器以及它有多少簿记。