What Happens When You Delete Large Python Objects?

When large objects are deleted in Python, the memory they occupied is freed from the application level but is not always returned to the operating system immediately. Python utilizes reference counting and an internal memory management system called PyMalloc, working alongside the C runtime allocator. While deleting a large object flags its memory as available for future Python operations, external monitoring tools may still show high process memory usage due to heap fragmentation and allocator retention policies.

Reference Counting and Object Destruction

Python primarily manages memory through reference counting. When you use the del keyword or an object falls out of scope, Python decrements that object's reference counter:

import sys

large_list = [i for i in range(10_000_000)]
del large_list

The del statement does not directly wipe memory; it destroys the name binding and reduces the reference count by one. If the count reaches zero, Python immediately invokes the object's deallocator function to free the underlying resources. For objects with circular references, Python's cyclical garbage collector periodically detects and clears them during background collection cycles.

PyMalloc vs. The System Allocator

Python distinguishes between small and large memory allocations to optimize performance:

Why the Operating System Does Not Reclaim the Memory

Even after a large object is cleared and its memory is passed back via free(), your system's activity monitor might show that Python is still using the same amount of RAM. This occurs for several reasons:

  1. Allocator Caching: The underlying C library allocator (such as glibc on Linux) often holds onto freed virtual memory rather than returning it to the kernel. It assumes the application will request large chunks of memory again soon, saving the performance overhead of repeated system calls.
  2. Heap Fragmentation: Memory allocated on the process heap via brk() can only shrink from the top down. If a single small, long-lived object sits at the highest address of the heap, none of the freed memory below it can be unmapped back to the operating system.
  3. Memory Mapping Boundaries: Memory allocated via mmap() (common for extremely large, contiguous buffers) can typically be returned directly to the kernel via munmap(). However, if the object was split or allocated via traditional heap expansion, it remains locked in the process space.

Forcing Memory Reclamation

If reclaiming memory is critical to your application's architecture, standard del commands may need to be paired with more aggressive cleanup strategies: