Помните недавнее нытье лексика о том, что рутноп съедает всю память и убивает железку? Так вот, я в это говно тоже сел.
Первой мыслью было, что бидон настолько замечательно аллоцирует объекты, что заставляет glibc'овский malloc фрагментировать память.
Я экспериментировал с openbsd'шным malloc, tcmalloc, но память не возвращалась.
Оказывается, в бидоне есть встроенный аллокатор, надстроенный над системным malloc, добавляющий еще один уровень умничанья и пулов "свободной" памяти.
Я собрал бидон --without-pymalloc и запустил с LD_PRELOAD=/usr/lib/libtcmalloc.so
Память не возвращалась.
Я сходил почитал комиксы в инторнете, потупил в чятики, глянул в процесслист и обнаружил, что RSS упало.
Предположение: Python таки освободил память, но tcmalloc решил, что память можно придержать на случай, если она еще понадобится.
Чтобы проверить это предположение, я написал биндинги к tcmalloc.
Создаем крупный объект из кучи мелких говен. generic.current_allocated_bytes растет
Удаляем его. generic.current_allocated_bytes падает, tcmalloc.pageheap_free_bytes растет
Делаем pytcm.release_free_memory(), tcmalloc.pageheap_free_bytes падает, tcmalloc.pageheap_unmapped_bytes растет, RSS падает, VSZ остается высоким.
Память действительно освободилась питоном, а tcmalloc сделал madvise(...,MADV_DONTNEED) на страницы из своего page heap.
Мораль: PYMALLOC говно. Если ты эмбеддор, это первое, что тебе нужно выдрать из питона.