Linux Allocator nie zwalnia małych fragmentów pamięci

Wydaje się, że alokator glibc dla Linuksa zachowuje się dziwnie. Mam nadzieję, że ktoś może rzucić na to trochę światła. Oto plik źródłowy, który mam:

first.cpp:

#include <unistd.h>
#include <stdlib.h>
#include <list>
#include <vector>

int main() {

  std::list<char*> ptrs;
  for(size_t i = 0; i < 50000; ++i) {
    ptrs.push_back( new char[1024] );
  }
  for(size_t i = 0; i < 50000; ++i) {
    delete[] ptrs.back();
    ptrs.pop_back();
  }

  ptrs.clear();

  sleep(100);

  return 0;
}

second.cpp:

#include <unistd.h>
#include <stdlib.h>
#include <list>

int main() {

  char** ptrs = new char*[50000];
  for(size_t i = 0; i < 50000; ++i) {
    ptrs[i] = new char[1024];
  }
  for(size_t i = 0; i < 50000; ++i) {
    delete[] ptrs[i];
  }
  delete[] ptrs;

  sleep(100);

  return 0;
}

Kompiluję oba:

$ g++ -o first first.cpp
$ g++ -o second second.cpp

Uruchamiam pierwszy, a po jego uśpieniu widzę rozmiar pamięci rezydentnej:

Kiedy kompiluję first.cpp i uruchamiam go, patrzę na pamięć za pomocą ps:

$ ./first&
$ ps aux | grep first
davidw    9393  1.3  0.3  64344 53016 pts/4    S    23:37   0:00 ./first


$ ./second&
$ ps aux | grep second
davidw    9404  1.0  0.0  12068  1024 pts/4    S    23:38   0:00 ./second

Zwróć uwagę na rozmiar pamięci rezydentnej. Po pierwsze, rozmiar pamięci rezydentnej wynosi 53016k. w drugim jest 1024k. Po raz pierwszy nigdy nie wydałem przydziałów z powrotem do jądra z jakiegoś powodu.

Dlaczego pierwszy program nie rezygnuje z pamięci do jądra, ale drugi program to robi? Rozumiem, że pierwszy program korzysta z listy połączonej, a lista połączona prawdopodobnie przydziela niektóre węzły na tej samej stronie, co dane, które zwalniamy. Jednak te węzły powinny zostać zwolnione, ponieważ wyłączamy te węzły, a następnie usuwamy połączoną listę. Jeśli uruchomisz któryś z tych programów przez valgrind, wróci bez przecieków pamięci. Prawdopodobnie dzieje się tak, że pamięć jest pofragmentowana w first.cpp, która nie jest w second.cpp. Jeśli jednak cała pamięć na stronie zostanie zwolniona, jak ta strona nie zostanie z powrotem przekazana do jądra? Czego potrzeba, aby pamięć została zwolniona z powrotem do jądra? Jak mogę zmodyfikować first.cpp (kontynuując umieszczanie znaków char * na liście), aby pamięć została przekazana do jądra.

questionAnswers(4)

yourAnswerToTheQuestion