Целенаправленно тратить всю основную память на изучение фрагментации

В моем классе у нас есть задание, и один из вопросов гласит:

Фрагментация памяти в C: спроектировать, реализовать и запустить C-программу, которая выполняет следующие действия: она выделяет память для последовательности из 3-х массивов размером 500000 элементов каждый; то он освобождает все массивы с четными номерами и выделяет последовательность из m массивов размером 700000 элементов каждый. Измерьте количество времени, которое требуется вашей программе для распределения первой последовательности и для второй последовательности. Выберите m, чтобы вы исчерпали всю основную память, доступную вашей программе. Объясните свои тайминги

Моя реализация этого заключается в следующем:

#include <iostream>
#include <time.h>
#include <algorithm>

void main(){
    clock_t begin1, stop1, begin2, stop2;
    double tdif = 0, tdif2 = 0;
    for(int k=0;k<1000;k++){
    double dif, dif2;
    const int m = 50000;
    begin1 = clock();
    printf("Step One\n");
    int *container[3*m];
    for(int i=0;i<(3*m);i++)
    {
        int *tmpAry = (int *)malloc(500000*sizeof(int));
        container[i] = tmpAry;
    }
    stop1 = clock();
    printf("Step Two\n");
    for(int i=0;i<(3*m);i+=2)
    {
    free(container[i]);
    }
    begin2 = clock();
    printf("Step Three\n");
    int *container2[m];
    for(int i=0;i<m;i++)
    {
    int *tmpAry = (int *)malloc(700000*sizeof(int));
    container2[i] = tmpAry;
    }
    stop2 = clock();
    dif = (stop1 - begin1)/1000.00;
    dif2 = (stop2 - begin2)/1000.00;
    tdif+=dif;
    tdif/=2;
    tdif2+=dif2;
    tdif2/=2;
}
printf("To Allocate the first array it took: %.5f\n",tdif);
printf("To Allocate the second array it took: %.5f\n",tdif2);
system("pause");
};

Я изменил это несколько разных способов, но консистенции, которые я вижу, это то, что, когда я изначально выделяю память для массивов элементов размером 3 * м * 500000, он использует всю доступную основную память. Но затем, когда я говорю, чтобы освободить их, память не возвращается обратно в ОС, поэтому, когда она идет на выделение массивов элементов m * 700000, она делает это в файле страницы (своп-память), поэтому на самом деле она не отображает фрагментацию памяти.

Приведенный выше код работает в 1000 раз и усредняет его, это занимает довольно много времени. Первая средняя последовательность составила 2.06913 секунды, а вторая - 0.67594 секунды. Для меня вторая последовательность должна занимать больше времени, чтобы показать, как работает фрагментация, но из-за использования подкачки это не происходит. Есть ли способ обойти это или я ошибаюсь в своем предположении?

Я спрошу профессора о том, что у меня есть в понедельник, но до тех пор любая помощь будет оценена.

Ответ 1

Многие реализации libc (я думаю, что glibc включен) не возвращают память обратно в ОС при вызове free(), но сохраняйте ее, чтобы вы могли использовать ее при следующем распределении без syscall. Кроме того, из-за сложности современных улочек страниц и виртуальной памяти вы никогда не можете быть уверены, где что-либо находится в физической памяти, что делает его практически невозможным для намеренного фрагментации (даже если он фрагментирован). Вы должны помнить, что вся виртуальная память и вся физическая память - это разные звери.

(Ниже приведено для Linux, но, вероятно, применимо к Windows и OSX)

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

Помните, что набор непрерывных виртуальных адресов практически никогда не является физически непрерывным на практике (или даже в памяти).