Memory management in Python involves a private heap containing all Python objects and data structures. The Python memory manager has different components which deal with various dynamic storage management aspects, like sharing, segmentation, preallocation or caching.
The Python interpreter performs management of the Python heap separate from the system heap. The allocation of heap space for Python objects and other internal buffers is performed on demand by the Python memory manager through the Python/C API functions.
Memory is allocated in the Python interpreter by different methods according to the type of the object to be created. Scalar types, such as integers and floats, use a simple free list memory allocation method.
Python memory architecture
As shown in the diagram below, layers above the red dotted line are owned by Python process.
The topmost layer shows that Python uses a portion of the memory for internal use and non-object memory. The other portion is dedicated to object storage.
Layer +2 is where the object allocator is actually used. It is called for every object allocation and deallocation (PyObject_New / Del). This layer is also the place where the cyclic garbage collector operates selectively on container objects. The object allocator is not called if object specific allocators implement a proprietary allocation scheme for ex.: integer
Integers and floats - these two object types allocate their own blocks of memory of approximately 1kB, which are allocated with the system malloc(). They avoid waste from pymalloc rounding the object size up to the nearest multiple of 8. These objects are then linked on to a simple free list. When an object is needed, one is taken from the list or a new block is allocated. When an object is freed, it is returned to the free list.
Allocation strategy abstract
For small requests of fewer than 512 bytes, the allocator sub-allocates blocks of memory. Small requests are grouped in size classes spaced 8 bytes apart as shown in the below table. Larger objects are routed to a standard C allocator.
Small object allocator uses three levels of abstraction — arena, pool, and block. Pymalloc allocates memory in 256 kB chunks, called arenas. The arenas provide memory for 64 pools and are divided into 4 kB pools, which are in turn subdivided into fixed sized blocks, as shown below. The blocks are returned to the application.
Pool
A collection of blocks of the same size is called a pool. Requests of a particular size are serviced from memory pools of 4K (one VMM page). There is a fixed-size allocator for each size class. The partially_allocated_arenas list keeps track of arenas that have pools available. The pool is taken from the first arena in the partially_allocated_arenas list as shown below. Pools and blocks are not allocating memory directly. They are using already allocated space from arenas.
Block
Block is a chunk of memory of a certain size that can be allocated, freed, or combined with adjacent chunks into larger ranges. Each block can keep only one Python object of a fixed size. The size of the block can vary from 8 to 512 bytes and must be a multiple of eight (i.e., use 8-byte alignment). For convenience, such blocks are grouped in 64 size classes as shown in above table.
Since these blocks, pools, and arenas are kept in Python's own heap, freeing a memory block merely marks it as available for future use in the interpreter. Freeing memory in Python does not immediately free the memory at the system level.
Garbage Collection
When objects are no longer needed, Python automatically reclaims memory from them. Garbage collector has two components, the reference counting collector and the generational garbage collector, known as gc module.
Python abstracts away a lot of details of the memory management so that you can work on the high level code.