452 MB and Climbing: A Second glibc malloc Bug Behind WavePy's OOM Kills
1. Summary
Three long-running encoder processes got OOM-killed at 20-30 GB despite disciplined del +
gc.collect() after every single unit of work. The Python heap was clean — the leak was one
layer down, in glibc’s allocator. By default glibc doesn’t use a fixed mmap threshold; it
raises the threshold every time a large mmap’d chunk is freed, on the bet that a similarly
large allocation is coming again soon. For a workload that repeatedly allocates and frees many
differently-sized, short-lived, multi-megabyte buffers, that bet is wrong: allocations get
progressively demoted onto a heap arena that never gives pages back to the OS. The fix is one
line — mallopt(M_MMAP_THRESHOLD, N) pins the threshold and disables the adaptive behavior for
the rest of the process’s life. This applies to any long-running process (Python or otherwise)
that cycles through many moderately-large, short-lived buffers — image tiles, video frames, ML
batches, whatever the buffers happen to be.