2013-10-15 2 views
0

Следующий пример кода C (взяты из http://bugs.python.org/issue19246) выполнен на Windows 7 64-битной, в то время как скомпилирован в 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 МБ. Однако процедуру стресса можно повторять непрерывно без ошибок памяти.

Вопросов:

  1. Как память может быть фрагментирован, если она полностью не используется?
  2. Как фрагментация памяти может быть исправлена ​​для этого процесса?
+0

Я предполагаю: 1 MB = 1048576, а не 1000000. Хотя это действительно не имеет большого значения здесь. –

+0

@WedaPashi Не обязательно, это зависит от контекста, это может означать и то, и другое. 1 MiB можно использовать для предотвращения путаницы, которая всегда равна 1048576. – orlp

+0

@ nightcracker: Хорошо сэр. Спасибо –

ответ

3

# 1

Это своего рода забавный вопрос, потому что мы никогда не говорим о фрагментированной памяти в использовании (давайте игнорировать кэш местности на данный момент). Фрагментация памяти становится проблемой, когда память доступна для распространения снова, но предыдущие распределения памяти, разделяющие пул памяти на более мелкие куски, требуют объединения блоков.

Я думаю, причина, по которой вы задаете этот вопрос, состоит в том, что вы привыкли к примеру наихудшего случая, где такой непрерывный блок памяти не существует, не перемещая ничего. Но даже в не худших случаях это может быть очень сложная проблема, обнаруживающая потенциальную операцию де-фрагментации, в результате чего распределитель задыхается от экземпляров проблемы, когда человек может легко увидеть решение.

Но чтобы ответить на ваш вопрос, рассмотреть этот выделенный фрагмент памяти:

aaaabbbbcccc 

Теперь a и c получить освободившуюся:

....bbbb.... 

И теперь b получает освобожденный:

....____.... 

Теперь у нас есть три непрерывных seperat e кусков памяти в пуле памяти - для этого требуется специальный код, и, по-видимому, распределитель дросселирует на этом.

# 2

Ответ на этот вопрос прост: дефрагментировать пул памяти. Насколько легко/сложно решить эту проблему, зависит от того, какой распределитель используется и сколько он делает.

+0

Итак, вы говорите, что некоторая часть памяти была выделена в блоке размера N, она никогда не может использоваться в блоках для размера больше, чем N? Я упомянул * не использовался * в вопросе, чтобы подчеркнуть это ** полностью ** * не использовался *. – peterdemin

+0

@peterdemin Можете ли вы поместить 16 байтов в 8 байт?Сначала вам нужно будет сгруппировать блоки вместе, но это требует бухгалтерского учета, который распределитель, о котором идет речь, не хочет платить (или распределитель неисправен). – orlp

+0

@peterdemin Кроме того, обратите внимание, что человек может быть умным и видеть, что есть 0 байт, поэтому мы могли бы бросить всю бухгалтерию так далеко и начать чистку. На практике это почти никогда не происходит, поэтому писать специальный код для этого не стоит. – orlp

Смежные вопросы