Memory Model and Garbage Collection

Python 3.14 · ✓ verified by execution on 2026-07-31

Understanding how Python manages memory helps you write more efficient code and avoid memory leaks. Python automatically handles memory allocation and deallocation, primarily through reference counting, backed by a cyclic Garbage Collector (GC).

Reference Counting

Every Python object lives on a private heap that the interpreter owns. You never allocate or free memory yourself — you create references, and the interpreter decides when the object behind them can go.

Python’s primary memory management mechanism is reference counting. Every object keeps a count of how many variables or data structures reference it. When this count drops to zero, the object is immediately deallocated.

You can inspect the reference count using sys.getrefcount(). Note that passing the object to getrefcount() temporarily increases the count by 1.

python · visualize
import sys
a = []
# One reference from 'a', one from the getrefcount argument
print(sys.getrefcount(a) == 2)
True

Cyclic Garbage Collection

Most objects are freed the instant their last reference disappears, by reference counting. The gc module exists for the case reference counting cannot solve: cycles, where two objects refer to each other and so never reach zero.

Reference counting has one major flaw: reference cycles. If object A references object B, and object B references object A, their reference counts will never drop to zero, even if your program no longer has access to them!

The gc module periodically scans for these isolated cycles and cleans them up.

python
import gc

class Node:
    pass

a = Node()
b = Node()

# Create a cyclic reference
a.child = b
b.parent = a

# Delete our variables, losing access to the objects
del a, b

# The cyclic garbage collector cleans them up
# collect() returns the number of unreachable objects found
print(gc.collect() > 0)
Output
True

You can even disable and enable the garbage collector manually, though this is rarely necessary unless you are trying to minimize pauses in highly latency-sensitive applications.

python
import gc
gc.disable()
print(gc.isenabled())
gc.enable()
print(gc.isenabled())
Output
False
True

Weak References

A weak reference points at an object without keeping it alive. That is what you want for caches: hold the entry while something else still needs it, and let it vanish the moment nothing does.

If you are building a cache, you might want to hold references to objects without keeping them alive. A weakref allows you to reference an object without incrementing its reference count!

python
import weakref

class MyClass:
    pass

obj = MyClass()
r = weakref.ref(obj)

# The weakref can be called to retrieve the original object
print(r() is obj)

# Once the original object is deleted, the weakref returns None
del obj
print(r() is None)
Output
True
True

Identity and Interning

id() returns the identity of an object — in CPython, its memory address. It is the fastest way to see whether two names point at the same object rather than at two equal ones.

The id() function returns a unique integer identifying an object’s memory address. The is operator is simply syntactic sugar for checking if two objects have the exact same id().

python
x = [1, 2]
y = [1, 2]

# They have equivalent data
print(x == y)

# But they are distinct objects in memory
print(x is y)
print(id(x) != id(y))
Output
True
False
True

Object Interning

Small integers between -5 and 256 are pre-created and reused, so a = 100; b = 100 gives you the same object twice. This is why is sometimes appears to work on numbers — and why relying on it is a bug waiting to happen with larger values.

To save memory and increase speed, CPython automatically reuses a single instance for certain immutable objects, like small integers and short strings. This is known as interning.

python
a = 256
b = 256
# Small integers are interned by CPython
print(a is b)

c = "hello_world"
d = "hello_world"
# Short identifier-like strings are also interned
print(c is d)
Output
True
True

Check yourself

What is Python's primary memory management mechanism?

Reveal answer

Reference counting — Python primarily uses reference counting. When an object's reference count drops to zero, it is immediately deallocated.

Why does Python include the `gc` module if it already uses reference counting?

Reveal answer

To find and clean up cyclic references. — Reference counting cannot detect reference cycles (e.g., when two objects reference each other). The gc module is a cyclic garbage collector designed to break these cycles.

What does the 'is' operator check in Python?

Reveal answer

If two references point to the exact same memory address (identity). — The 'is' operator checks object identity (what id() returns). Two separate lists with the same values will return True for == but False for 'is'.

Challenges

Challenge 1 +100 XP

Create a Node object and a weak reference 'w' to it. Print True if w() is not None, then delete the Node object and print True if w() is None.

python
  • Test 1 — expects "True\nTrue\n"
Need a hint? (−25% XP)

Assign the weakref to w, delete obj, and invoke w() to see if it returned None.

Show solution (0 XP)
import weakref
class Node:
    pass
obj = Node()
w = weakref.ref(obj)
print(w() is not None)
del obj
print(w() is None)

Challenge 2 +100 XP

Disable the garbage collector, print its enabled status, then re-enable it and print its enabled status again.

python
  • Test 1 — expects "False\nTrue\n"
Need a hint? (−25% XP)

Use gc.disable(), gc.enable(), and gc.isenabled().

Show solution (0 XP)
import gc
gc.disable()
print(gc.isenabled())
gc.enable()
print(gc.isenabled())