Python
Memory management
Python Implementation
Python is an interpreted & high-level language. It is dynamically typed & is used as a general-purpose language. The default python implementation is CPython & is written in C programming language.
The CPython converts your python code into computer-readable instructions called bytecode. These instructions are interpreted by a virtual machine when you run your code. The bytecode is stored in .pyc file in __pycache__ folder.
Other python implementations available are:
- Jython: compiles down to Java bytecode to run on JVM (Java Virtual Machine)
- IronPython: compiles down to run on Microsoft’s CLR (Common Language Runtime)
- PyPy: uses JIT (Just-In-Time) compilation to translate python code into assembly language
If you want to look at CPython's bytecode then you can. Here's how you can:

Now, let's have a look at the above code. Lines 1 to 6 are a function definition. In line 8, we import the 'dis' module which can be used to view the intermediate Python bytecode (or you can say, disassembler for Python bytecode) that is generated by CPython (interpreter).
CPython Memory management
In this article we are only going to talk about CPython implementation for memory management. As CPython is written in C language it does not natively support OOP. In Python, everything is an object, even int or str. CPython uses a struct called PyObject to store this information.
PyObject contains two things:
- ob_refcnt: reference count
- ob_type: pointer to another type
The reference count increases whenever that object is referenced. For e.g.

Here, the reference count of ‘198653’ is increased & decreased whenever it is being referenced or dereferenced by the variables.
NOTE: The reference count is starting with 3 because CPython internally keeps an already created integer object with value 1 and internal variables point to it.
Whenever the reference count of an object reaches to 0. That object can be picked up by the garbage collector so that memory can be freed.
The OS provides virtual memory layer that applications like python can access. Python uses some portion for internal use & other portion is dedicated for object storage. CPython used an object allocator which gets called every time a new object is created or deleted. Internally the object allocator used C library’s malloc function for memory allocation.
There are 3 main pieces of CPython’s memory allocator:
- Arenas
- Pools
- Blocks
Arenas
Arenas are the largest chunks of memory. They are aligned on fixed-length contiguous chunk of memory that OS uses. Python assumes the system’s page size as 256KB. Arenas contains pools.
They are organized into a doubly linked list called usable_arenas. This list is sorted by number of free pools available. Whenever memory is freed python doesn’t actually free the memory to OS it keeps it allocated & will use it for newer data. The arenas which are closer to being empty should be allowed to become empty so that chunk of memory can then be truly freed & can be given back to OS reducing the overall memory used by Python.

Pools
Pools are present inside arenas & have virtual memory of 4 KB. They are composed of blocks of a single size class. Each pool maintains a doubly linked list to other pools of same size class. The pools can be in the following states:
- empty: No data stored & any size class blocks can be assigned as per the need
- used: Blocks for a particular size class are available to store data
- full: All blocks are allocated & contain data
There are 2 types of lists used here:
- freepools: Keeps track of all the pools in empty state
- usedpools: Keeps track of all the pools in used state
Now if a full pool frees some of its blocks then that pool will be added into the usedpools list of that particular size class. And if the program requires a memory chunk of some bytes & no pools of that size class is available then a new pool from the freepools list will be used for that & that pool will now be added to usedpools list & removed from freepools list.

Blocks
Pools are fragmented in smaller blocks of memory. All the blocks in a pool are of same size class. Blocks are the place where the actual objects are stored. Pools contain pointer to their free blocks. The freeblock pointer points to a singly linked list of free blocks of memory which is not always contiguous.
Blocks can have 3 states:
- untouched: portion of memory that is not yet allocated for use
- free: portion of memory that was previously allocated but it no longer contains relevant data
- allocated: portion of memory that actually contains relevant data
Whenever the required memory exceeds the available free blocks the allocater will get some untouched blocks into the pool. Whenever the blocks become free they are added to the freeblock list at the front.
