Утечка памяти при выделении страницы с помощью malloc

рассмотрим следующий код C, который создает 100 000 страниц размером 4 КБ, затем освобождает 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;
}

Если вы скомпилируете его, запустите его и контролируете использование памяти процесса, вы можете увидеть, что использование памяти достигает около 400 МБ до первого getchar (когда память выделяется на 100 000 страниц), то он сохраняет то же самое даже после того, как 99,999 страниц де-выделены (после второго getchar) и, наконец, он падает до 1 МБ, когда последний страница де-выделена.

Итак, мой вопрос: почему это происходит? Почему вся память возвращается в ОС только тогда, когда все страницы будут освобождены? Есть ли размер страницы или выравнивание страницы, которое предотвращает подобные вещи? Я имею в виду, есть ли какой-либо размер страницы или выравнивание, чтобы любая страница malloced полностью возвращалась в операционную систему, когда освобождается только одна страница?

1 ответов


это полностью зависит от реализации, но я считаю, что это связано с тем, как память распределитель работ. Как правило, когда менеджеру памяти требуется больше памяти из ОС, он вызывает 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)

другими словами, поскольку память выделяется как стек, пока вы не освободите последнее, что вы выделили, ОС не сможет вернуть память.

надеюсь, что это помогает!