When does CPython return unused RSS memory to the OS?

Viewed 108

I am trying to determine the conditions that causes CPython to release unused RSS memory back to the underlying OS?

From the pypy official docs it says:

Indeed, returning memory to the OS is a hard and not solved problem. In PyPy, it occurs only if an arena is entirely free—a contiguous block of 64 pages of 4 or 8 KB each.

What are the conditions for CPython to release unused RSS memory back to the underlying OS?

NOTE:
I know I can use tracemalloc to profile memory usage, my question is more related to how/why RSS memory is released back to the underlying OS for CPython?

1 Answers

When memory is given back to OS, depends on which memory manager the Python interpreter uses. Usually it is not directly the memory-manager provided by the C runtime, but pymalloc-allocator whose details are probably best described in the code itself.

For memory sizes > 512bytes, pymalloc will delegate the memory request to the underlying C-memory manager. So if you allocate a big array.array, the C-memory manager decides when the memory goes back to OS, once the array.array-object is destroyed.

However, when you have a lot of objects smaller than 512 bytes, the pymalloc is responsible for them. Pre-allocated memory of the pymalloc-allocators consist of so called arenas, which are 1MB in size (for 64bit systems USE_LARGE_ARENAS is defined, for 32bit arena size is 256KB). At the beginning an arena allocates 1MB of virtual memory, which will be committed by the OS as the really used memory grows.

Each arena consist of so called pools: every pool can hold objects of special sizes. When we look in the code, we see, that only when every pool in an arena is empty the reserved memory for the arena is returned to the C-memory management, this is described here:

/* All the rest is arena management.  We just freed
 * a pool, and there are 4 cases for arena mgmt:
 * 1. If all the pools are free, return the arena to
 *    the system free().  Except if this is the last
 *    arena in the list, keep it to avoid thrashing:
 *    keeping one wholly free arena in the list avoids
 *    pathological cases where a simple loop would
 *    otherwise provoke needing to allocate and free an
 *    arena on every iteration.  See bpo-37257.
 * 2. If this is the only free pool in the arena,
 *    add the arena back to the `usable_arenas` list.
 * 3. If the "next" arena has a smaller count of free
 *    pools, we have to "slide this arena right" to
 *    restore that usable_arenas is sorted in order of
 *    nfreepools.
 * 4. Else there's nothing more to do.
 */

Returning an arena to C-memory management doesn't enforce that the memory is returned to the OS, but in most cases 1MB is big enough that the C-memory manager will not "sit" on it, but return it immediately to OS.


In a nutshell: it is not easy to predict when memory is really returned to OS, as only chunks of 1MB can be returned by pymalloc, but this happens when the whole chunk is unused, which might be prevented by even one object which is alive and lives in this otherwise empty arena.

Related