Reference Counting in CPython

RoadmapsPython

Overview

In CPython, every object keeps a count of how many labels (variables, list elements, attributes) point to it. This is called the reference count. When a reference count drops to zero, the object is immediately destroyed and its memory is reclaimed. import sys my_list = [1, 2, 3] # Returns 2 (1 for my_list, 1 for getrefcount argument) print(sys.getrefcount(my_list)) Think of an object as a balloon and references as strings holding it down. Every time you assign a variable or append it to a list, you tie a new string to the balloon. Every time a variable goes out of scope, you cut a string. When the last string is cut, the balloon flies away. This deterministic memory management ensures resources are freed the exact moment they are no longer needed. node_a = {} node_b = {} node_a['next'] = node_b node_b['prev'] = node_a del node_a, node_b **Edge Case**: Circular references prevent the reference count from ever reaching zero. The objects keep each other alive until the generational garbage collector eventually intervenes.

Understanding reference counting explains: - When Python cleans up objects - Why `__del__` is called (or not called) - How circular references can cause memory leaks

Where used: Memory optimization, Writing C extensions, Debugging with `objgraph`

Why learn this

Code walkthrough

import sys

data = {'key': 'value'}
print(sys.getrefcount(data))

alias = data
print(sys.getrefcount(data))

del alias
print(sys.getrefcount(data))

Focus: The reference count explicitly increments when `alias` is created and decrements when `alias` is deleted.

Aha moment

class Tracked:
    def __del__(self):
        print('Destroyed!')

obj = Tracked()
alias = obj
del obj
print('End of script')

Prediction: When does `'Destroyed!'` print? Before or after `'End of script'`?

Common guess: Before `'End of script'`, because we called `del obj`.

It prints AFTER `'End of script'`. `del obj` removes the name `obj` and decrements the reference count to `1` (because `alias` still points to it). The object isn't destroyed until the script ends and `alias` is cleaned up.

Common mistakes

Glossary

reclaimed
The process where a computer takes back memory that is no longer being used by a program so it can be used for something else. Example: memory is freed.
circular references
When two or more objects hold references to each other, preventing the system from realizing they are no longer needed. Example: `a.child = b; b.parent = a`.

Recall questions

Understanding checks

What does this script print?

It prints `'Done!'` followed by `'Closed!'`.

`del conn` only removes the label `conn` and drops the reference count by `1`. Since `backup` still points to the object, its reference count is not zero. The object is finally destroyed and prints `'Closed!'` when the script ends and `backup` is cleaned up.

Why might memory not be freed immediately when two objects hold references to each other, even if no variables point to them?

They form a circular reference, preventing their reference counts from ever reaching zero.

Reference counting only reclaims an object when its count hits exactly `0`. In a circular reference, each object keeps the other's count at `1`, so the memory won't be freed until the generational garbage collector runs.

Practice tasks

Observe reference counting with `sys.getrefcount`

Modify the starter code to create a list `[1, 2, 3]` and assign it to a variable `a`. Print the reference count of `a` using `sys.getrefcount(a)`. Then, create a second variable `b` that points to `a`, and print the reference count again. Note: `sys.getrefcount` temporarily increases the count by `1` because the function argument itself creates a reference.

Challenge

Tracking Object Destruction

Create a class `Resource` with an `__init__` method that prints `'Created'` and a `__del__` method that prints `'Destroyed'`. Write a short script where you instantiate a `Resource` and assign it to a variable `x`. Then assign `x` to another variable `y`. Delete `x` and print `'x deleted'`. Finally, delete `y` and print `'y deleted'`. Observe exactly when `'Destroyed'` is printed.

Continue learning

Previous: Object identity: id() and memory references

Previous: Everything is an object in Python

Return to Python Roadmap