Course outline · 0% complete

0/28 lessons0%

Course overview →

Garbage collection, gently

lesson 4-3 · ~10 min · 13/28

Who cleans up the heap

In lesson 4-1, heap objects live as long as anything points at them. Someone has to notice when nothing points at an object anymore and reclaim that memory.

In C, that someone is you, through malloc and free, and forgetting is a leak. In Python, Java, JavaScript, and Go, it is the garbage collector, usually abbreviated GC.

Python's main strategy is reference counting. Every object carries a count of how many references point at it. Assignment adds one, and deleting a name or a name going out of scope removes one. The moment the count hits zero, the object is freed immediately.

EventEffect on the count
s = r+1
del r-1
a function returns-1 for each local name
count reaches 0the object is freed right there

The immediacy is unusual. Many collectors free memory later, at a moment of their own choosing, so Python's behavior is observable with __del__, a method Python calls right when an object is being freed.

r and s both point at itcount 2alivedel rcount 1still alivedel sa container counts too, so a list holding the object keeps it alivethe classic leak is a global list nobody ever clearscount 0freed right here
The object is freed the instant its reference count reaches zero, not later.

Watching a count reach zero

The order of the printed lines is the whole lesson.

class Resource:
    def __init__(self, name):
        self.name = name
        print("created", self.name)

    def __del__(self):
        print("freed", self.name)

r = Resource("A")
s = r
del r
print("still alive, s still points to it")
del s
print("after deleting the last reference")

Output

created A
still alive, s still points to it
freed A
after deleting the last reference

The object survives del r because s still points at it, so the count went from 2 to 1 rather than to 0.

freed A prints before the final line, which is the proof of immediacy. The count hit zero at del s and reference counting freed the object at that instant, not at the end of the program.

A container holds a reference too

A list counts as a reference, so deleting the name is not enough to free the object.

class Resource:
    def __init__(self, name):
        self.name = name
        print("created", self.name)

    def __del__(self):
        print("freed", self.name)

r = Resource("B")
box = [r]
del r
print("still alive inside the list")
box.clear()
print("after the list let go")

Output

created B
still alive inside the list
freed B
after the list let go

box = [r] means the list holds a reference of its own, so del r leaves the count at 1 and the object lives on.

box.clear() drops the list's reference, the count hits zero, and freed B prints right there. This is the mechanism behind every cache-shaped memory leak: the collection is the surviving reference nobody remembered.

What this means for your code

Three consequences carry into real systems.

Leaks still happen in garbage-collected languages. The GC frees only unreachable objects, so a global list or cache that keeps growing keeps everything in it reachable forever. That is the slow-OOM pattern from lesson 4-2.

Reference counting has one blind spot, which is cycles. If A points to B and B points to A, their counts never reach zero even when nothing else can reach them. Python runs a second, occasional cycle collector to catch exactly this case.

Garbage collection costs CPU time. That is the trade, with C fast and manual while Python is safe and pays a tax.

LanguageWho frees memoryFailure mode
Cyou, by handforgetting to free, or freeing twice
Pythonreference counts plus a cycle collectorforgetting to stop referencing

For most software the safety is worth the tax, and the interesting point is that the failure mode moved rather than disappeared.

Why a debug list defeats the collector

A Python service that stores every request it has ever seen in a module-level list will grow forever, and the garbage collector cannot help.

The GC frees only unreachable objects, and reachable means still accessible through some chain of references from live variables. Everything inside a live global list stays reachable, so the GC must keep it, since it cannot know the list will never be read again.

SituationGC verdict
object in a live global listreachable, keep
object whose last name went out of scopeunreachable, free

This is the classic leak in a garbage-collected language. The mistake is not forgetting to free, it is forgetting to stop referencing, and the fix is a bounded structure such as a fixed-size deque or a cache with eviction.