How Python Uses Reference Counting to Free Memory
Python manages memory automatically primarily through reference counting, a deterministic mechanism where the runtime tracks how many aliases or references point to an object. Each time an object is referenced, its internal counter increments; when a reference is removed, the counter decrements. The moment an object's reference counter hits zero, its memory is immediately deallocated and returned to the Python memory pool or the operating system, ensuring predictable and low-latency cleanup for the majority of runtime allocations.
The Reference Counter:
ob_refcnt
At the C level (CPython), every object is represented by a structure
based on PyObject. Inside this base structure exists a
field named ob_refcnt. This integer field stores the
current total number of active references to that specific object in
memory.
How References Increase
An object’s ob_refcnt is incremented automatically in
several common scenarios:
- Assignment: Assigning an object to a new variable
name (e.g.,
b = a). - Data structures: Adding an object to a collection,
such as a
list,dict,set, ortuple. - Function calls: Passing the object as an argument into a function creates a new local reference in the function's scope.
- Attribute binding: Storing the object as an attribute of an instance or class.
How References Decrease
An object’s ob_refcnt is decremented when any existing
reference is broken:
- Scope exit: A local variable leaves its scope (for example, when a function finishes executing, all its local variables are cleared).
- Reassignment: A variable name is bound to a
different object (e.g.,
x = 5followed byx = 10decrements the count of5). - Explicit deletion: Using the
delstatement removes the variable binding rather than the object directly, decrementing the object's counter. - Container removal: Removing an element from a list, dictionary, or other collection.
The Deallocation Process
The defining feature of reference counting is immediate reclamation:
- As soon as an operation causes
ob_refcntto transition from 1 to 0, Python invokes the type-specific deallocator (tp_dealloc) defined in the object's type structure. - The deallocator decrements the reference counts of any other objects that this object referenced.
- The memory block occupied by the object is cleared and marked as available for future allocations within Python's internal memory manager (PyMalloc) or returned to the system heap.
Because deallocation happens immediately upon the last reference being lost, objects often release associated system resources (such as file handles or network sockets) without waiting for a scheduled collection phase.
Circular References and the Cyclic Garbage Collector
While reference counting is fast and predictable, it cannot reclaim memory involved in circular references on its own. A reference cycle occurs when two or more objects reference each other (e.g., object A points to object B, and object B points to object A), but neither object can be reached from anywhere else in the application.
In this scenario, both objects maintain an ob_refcnt of
at least 1, preventing standard reference counting from ever reclaiming
them. To solve this, Python supplements reference counting with a
secondary, generational cyclic garbage collector. This collector runs
periodically, detects self-referential groups that are isolated from
root scopes, breaks the cycles, and forces the deallocation of the
unreachable objects.