Skip to the solution
HardDSA15 min read

How does Python manage memory and garbage collection?

1.1k views

The problem

Question presented to candidate: "You never call free() in Python, yet memory does get released. Explain how CPython manages memory: where objects live, what decides when one is freed, why reference counting alone is not enough, and what the gc module actually does."

What a strong answer should cover:

  • Every object lives on a private heap managed by the interpreter; names and containers hold references, never the objects themselves.
  • Reference counting is the primary mechanism: each object has a count, and CPython frees it the moment the count drops to zero (deterministic, immediate, no pause).
  • Reference cycles never reach zero, so a separate cyclic garbage collector (the gc module) periodically finds unreachable groups of container objects; it is generational (three generations, default thresholds (700, 10, 10) on 3.11 and 3.12) and only tracks containers.
  • The allocator: pymalloc serves small requests (up to 512 bytes) from pools inside arenas, larger ones go to the system malloc, so freed memory is reused by Python but not always returned to the OS.
  • Practical tools and gotchas: sys.getrefcount, sys.getsizeof (shallow), tracemalloc, gc.collect(), gc.disable()/gc.freeze(); immortal objects in 3.12 (PEP 683); this is CPython behaviour, PyPy uses a different collector.

Clarifying questions expected:

  • Do you mean CPython specifically, or Python implementations in general (PyPy, the free-threaded build)?
  • Is this about understanding the model, or about a concrete problem such as a memory leak or high RSS in a long-running service?

Code / implementation expected: Optional โ€” sys.getrefcount deltas and a two-object cycle that only gc.collect() frees are the classic demonstration.

  • memory
  • gc

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

15 min read ยท 12 sections

Run the code

Edit the code and run it here. Each console line with an expected value becomes a test.

PythonReference counting, a cycle only gc.collect() frees, and what gc tracks
PythonGenerations, gc.callbacks and tracemalloc
PythonImmortal objects on Python 3.12 (PEP 683)
# Python 3.12+: some objects are immortal (PEP 683). Their refcount is pinned,
# so taking new references to them no longer writes to the object.
import sys

def refs_added(obj, n=1000):
    before = sys.getrefcount(obj)
    holders = [obj] * n
    after = sys.getrefcount(obj)
    return after - before

class Plain:
    pass

print(sys.version_info[:2] >= (3, 12))
print("None:", refs_added(None))
print("True:", refs_added(True))
print("small int 7:", refs_added(7))
print("empty tuple:", refs_added(()))
print("ordinary object:", refs_added(Plain()))
print("refcount of None is huge:", sys.getrefcount(None) > 10**9)

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 118 of 139 in Python

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Python questions
Up nextHard

What is an async iterator and async for?

Continue