Alocador do Linux não libera pequenos pedaços de memória

O alocador glibc do Linux parece estar se comportando de maneira estranha. Espero que alguém possa lançar alguma luz sobre isso. Aqui está o arquivo de origem que eu tenho:

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;
}

Eu compilar os dois:

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

Eu corro primeiro, e depois de dormir, vejo o tamanho da memória residente:

Quando eu compilo first.cpp, e corro, eu olho para a memória com 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

Observe o tamanho da memória residente. Em primeiro lugar, o tamanho da memória residente é 53016k. em segundo lugar, é 1024k. Primeiro, nunca liberou as alocações de volta ao kernel por algum motivo ou outro.

Por que o primeiro programa não abre mão da memória para o kernel, mas o segundo programa faz isso? Entendo que o primeiro programa usa uma lista vinculada e a lista vinculada provavelmente aloca alguns nós na mesma página que os dados que estamos liberando. No entanto, esses nós devem ser liberados, pois estamos removendo esses nós e, em seguida, limpando a lista vinculada. Se você executar um desses programas por meio do valgrind, ele voltará sem vazamentos de memória. O que provavelmente está acontecendo é que a memória fica fragmentada em first.cpp que não está em second.cpp. No entanto, se toda a memória em uma página for liberada, como essa página não será devolvida ao kernel? O que é preciso para a memória ser devolvida ao kernel? Como eu posso modificar first.cpp (continuando a colocar o char * 's em uma lista) para que a memória seja liberada para o kernel.

questionAnswers(4)

yourAnswerToTheQuestion