El asignador de Linux no libera pequeños trozos de memoria

El asignador de Linux glibc parece comportarse de manera extraña. Con suerte, alguien puede arrojar algo de luz sobre esto. Aquí está el archivo fuente que tengo:

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

Yo compilo tanto

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

Primero corro, y después de dormir, veo el tamaño de la memoria residente:

Cuando compilo first.cpp, y lo ejecuto, miro la memoria con 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 el tamaño de la memoria residente. En primer lugar, el tamaño de la memoria residente es 53016k. en segundo lugar, es 1024k. First nunca liberó las asignaciones de nuevo al kernel por alguna razón u otra.

¿Por qué el primer programa no entrega la memoria al kernel, pero el segundo programa sí? Entiendo que el primer programa usa una lista enlazada y la lista enlazada probablemente asigna algunos nodos en la misma página que los datos que estamos liberando. Sin embargo, esos nodos deben liberarse, ya que estamos eliminando esos nodos y luego borrando la lista enlazada. Si ejecuta cualquiera de estos programas a través de valgrind, regresa sin pérdidas de memoria. Lo que probablemente está sucediendo es que la memoria se fragmenta en first.cpp y no en second.cpp. Sin embargo, si se libera toda la memoria de una página, ¿cómo no se puede devolver esa página al núcleo? ¿Qué se necesita para que la memoria se entregue de vuelta al kernel? ¿Cómo puedo modificar first.cpp (continuando colocando los caracteres * en una lista) para que la memoria se entregue al kernel?

Respuestas a la pregunta(4)

Su respuesta a la pregunta